#include "builddefines.h"

#ifdef PRECOMPILEDHEADERS
	#include "Strategic All.h"
	#include "Loading Screen.h"
	#include "Enemy Soldier Save.h"
	#include "points.h"
#else
	#include "strategicmap.h"
	#include "strategic.h"
	#include "Strategic Mines.h"
	#include "types.h"
	#include "memory.h"
	#include <stdio.h>
	#include <stdarg.h>
	#include <math.h>
	#include <time.h>
	#include "jascreens.h"
	#include "worlddef.h"
	#include "Soldier Control.h"
	#include "overhead.h"
	#include "interface panels.h"
	#include "isometric utils.h"
	#include "Ambient Control.h"
	#include "worlddef.h"
	#include "worlddat.h"
	#include "text.h"
	#include "Soldier add.h"
	#include "soldier macros.h"
	#include "Strategic Pathing.h"
	#include "soldier create.h"
	#include "handle UI.h"
	#include "faces.h"
	#include "renderworld.h"
	#include "gamescreen.h"
	#include "game clock.h"
	#include "Soldier Init List.h"
	#include "strategic turns.h"
	#include "merc entering.h"
	#include "Map Information.h"
	#include "Assignments.h"
	#include "message.h"
	#include "Font Control.h"
	#include "environment.h"
	#include "Game Events.h"
	#include "quests.h"
	#include "FileMan.h"
	#include "animated progressbar.h"
	#include "music control.h"
	#include "fade screen.h"
	#include "Strategic Movement.h"
	#include "Campaign Types.h"
	#include "sys globals.h"
	#include "Exit Grids.h"
	#include "Tactical Save.h"
	#include "pathai.h"
	#include "animation control.h"
	#include "squads.h"
	#include "worldman.h"
	#include "Overhead.h"
	#include "Strategic Town Loyalty.h"
	#include "Queen Command.h"
	#include "cursor control.h"
	#include "PreBattle Interface.h"
	#include "Shade Table Util.h"
	#include "Scheduling.h"
	#include "gameloop.h"
	#include "Random.h"
	#include "ai.h"
	#include "opplist.h"
	#include "keys.h"
	#include "Tactical Placement GUI.h"
	#include "Map Screen Helicopter.h"
	#include "map edgepoints.h"
	#include "Player Command.h"
	#include "Event Pump.h"
	#include "meanwhile.h"
	#include "air raid.h"
	#include "Strategic Mines.h"
	#include "Creature Spreading.h"
	#include "SaveLoadMap.h"
	#include "Militia Control.h"
	#include "gamesettings.h"
	#include "dialogue control.h"
	#include "Town Militia.h"
	#include "sysutil.h"
	#include "Sound Control.h"
	#include "points.h"
	#include "render dirty.h"
	#include "Debug Control.h"
	#include "expat.h"
	#include "Loading Screen.h"
	#include "Queen Command.h"
	#include "Enemy Soldier Save.h"
	#include "Boxing.h"
	#include "NPC.h"
	#include "Strategic Event Handler.h"
	#include "MessageBoxScreen.h"
	#include "interface dialogue.h"
	#include "Map Screen Interface.h"
	#include "history.h"
	#include "Bullets.h"
	#include "physics.h"
	#include "Explosion Control.h"
	#include "Auto Resolve.h"
	#include "cursors.h"
#endif

#include "LuaInitNPCs.h"
#include "Luaglobal.h"

#ifdef JA2UB
#include "SaveLoadGame.h"
#include "email.h"
#include "Ja25 Strategic Ai.h"
#include "Merc Hiring.h"
#include "Ja25_Tactical.h"
#include "Timer Control.h"
#include "Soldier Control.h"
#include "Ja25Update.h"
#include "Map Screen Interface Bottom.h"
#include "Strategic AI.h"
#include "legion cfg.h"
#include "Luaglobal.h"
#endif

#include "connect.h" //hayden added alot ""'s to get around client spawing random/different placed AI
#include "SaveLoadGame.h"
#include "Strategic Mines.h"
#include "Strategic Mines LUA.h"
#include "UndergroundInit.h"

#include "LuaInitNPCs.h"
#include "Luaglobal.h"

#include "sgp_logger.h"

//forward declarations of common classes to eliminate includes
class OBJECTTYPE;
class SOLDIERTYPE;


//Used by PickGridNoToWalkIn
#define MAX_ATTEMPTS	200

#define		QUEST_CHECK_EVENT_TIME							( 8 * 60 )
#define		BOBBYRAY_UPDATE_TIME							  ( 9 * 60 )
#define		INSURANCE_UPDATE_TIME								0
#define		EARLY_MORNING_TIME									( 4 * 60 )
#define   ENRICO_MAIL_TIME                    ( 7 * 60 )

enum
{
	ABOUT_TO_LOAD_NEW_MAP,
	ABOUT_TO_TRASH_WORLD,
};
BOOLEAN HandleDefiniteUnloadingOfWorld( UINT8 ubUnloadCode );

extern INT32		gsRobotGridNo;
extern BOOLEAN	gfUndergroundTacticalTraversal;

//extern MINE_LOCATION_TYPE gMineLocation[MAX_NUMBER_OF_MINES];
//extern MINE_STATUS_TYPE gMineStatus[MAX_NUMBER_OF_MINES];


extern void CalculateNonPersistantPBIInfo();

extern void MapScreenDefaultOkBoxCallback( UINT8 bExitValue );

extern BOOLEAN		gfGettingNameFromSaveLoadScreen; // symbol already declared globally in SaveLoadScreen.cpp (jonathanl)

INT16			gWorldSectorX = 0;
INT16			gWorldSectorY = 0;
INT8			gbWorldSectorZ = -1;

INT16			gsAdjacentSectorX, gsAdjacentSectorY;
INT8			gbAdjacentSectorZ;
GROUP			*gpAdjacentGroup = NULL;
UINT8			gubAdjacentJumpCode;
UINT32		guiAdjacentTraverseTime;
UINT8			gubTacticalDirection;
INT32			gsAdditionalData;//dnl ch56 151009
INT32		gusDestExitGridNo;

BOOLEAN		fUsingEdgePointsForStrategicEntry = FALSE;
BOOLEAN		gfInvalidTraversal = FALSE;
BOOLEAN		gfLoneEPCAttemptingTraversal = FALSE;
BOOLEAN		gfRobotWithoutControllerAttemptingTraversal = FALSE;
BOOLEAN   gubLoneMercAttemptingToAbandonEPCs = 0;
INT8			gbPotentiallyAbandonedEPCSlotID = -1;

INT8 gbGreenToElitePromotions = 0;
INT8 gbGreenToRegPromotions = 0;
INT8 gbRegToElitePromotions = 0;
INT8 gbMilitiaPromotions = 0;


extern BOOLEAN gfUsePersistantPBI;

BOOLEAN		gfUseAlternateMap = FALSE;
// whether or not we have found Orta yet
BOOLEAN fFoundOrta = FALSE;

UINT8	NUMBER_OF_SAMS;

// have any of the sam sites been found (actual values, saved to savegames)
BOOLEAN fSamSiteFound[ MAX_NUMBER_OF_SAMS ];

// original of previous for proper initialization of a new game
BOOLEAN fSamSiteFoundOrig[ MAX_NUMBER_OF_SAMS ];

// sectors with sam sites
INT16 pSamList[ MAX_NUMBER_OF_SAMS ];

// girdno's of control terminal of sam site
INT32 pSamGridNoAList[ MAX_NUMBER_OF_SAMS ];
INT32 pSamGridNoBList[ MAX_NUMBER_OF_SAMS ];

// ATE: Update this w/ graphic used
// Use 3 if / orientation, 4 if \ orientation
INT8 gbSAMGraphicList[ MAX_NUMBER_OF_SAMS ];

INT8 gbMercIsNewInThisSector[ MAX_NUM_SOLDIERS ];


// the amount of time that a soldier will wait to return to desired/old squad
#define DESIRE_SQUAD_RESET_DELAY 12 * 60

BOOLEAN localizedMapTextOnly;


UINT8 ubSAMControlledSectors[ MAP_WORLD_Y ][ MAP_WORLD_X ];// = {
//       1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
//    0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0,
//
//    0,   1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 2, 2, 2, 2, 2, 2,   0,		// A
//    0,   1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,   0,		// B
//    0,   1, 1, 1, 1, 1, 1, 1, 3, 2, 2, 2, 2, 2, 2, 2, 2,   0,		// C
//    0,   1,01, 1, 1, 1, 1, 1, 3, 3, 2, 2, 2, 2, 2,02, 2,   0,		// D
//    0,   1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2,   0,		// E
//    0,   1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2,   0,		// F
//    0,   1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2,   0,		// G
//    0,   1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2,   0,		// H
//    0,   1, 1, 3, 3, 3, 3, 3,03, 3, 3, 3, 3, 3, 2, 2, 2,   0,		// I
//    0,   1, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2,   0,		// J
//    0,   4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2,   0,		// K
//    0,   4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2,   0,		// L
//    0,   4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2,   0,		// M
//    0,   4, 4, 4,04, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2,   0,		// N
//    0,   4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2,   0,		// O
//    0,   4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2,   0,		// P
//
//    0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0,
//};

INT16 DirXIncrementer[8] =
 {
  0,        //N
	1,        //NE
	1,        //E
	1,        //SE
	0,         //S
	-1,       //SW
	-1,       //W
	-1       //NW
 };

INT16 DirYIncrementer[8] =
 {
  -1,        //N
	-1,        //NE
	0,        //E
	1,        //SE
	1,         //S
	1,       //SW
	0,       //W
	-1       //NW
 };


STR8 pVertStrings[]={
"X",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
};

STR8 pHortStrings[]={
"X",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12",
"13",
"14",
"15",
"16",
"17",
};

// HEADROCK HAM 3.6: Array to hold user-defined sector names
CHAR16 gzSectorNames[256][4][MAX_SECTOR_NAME_LENGTH];

// moved to lua
//CHAR16 gzSectorUndergroundNames1[256][4][MAX_SECTOR_NAME_LENGTH]; 
//CHAR16 gzSectorUndergroundNames2[256][4][MAX_SECTOR_NAME_LENGTH]; 
//CHAR16 gzSectorUndergroundNames3[256][4][MAX_SECTOR_NAME_LENGTH]; 

BOOLEAN ReadInStrategicMapSectorTownNames(STR fileName, BOOLEAN localizedVersion);

void DoneFadeOutAdjacentSector( void );
void DoneFadeOutExitGridSector( void );

INT32 PickGridNoNearestEdge( SOLDIERTYPE *pSoldier, UINT8 ubTacticalDirection );
INT32 PickGridNoToWalkIn( SOLDIERTYPE *pSoldier, UINT8 ubInsertionDirection, UINT32 *puiNumAttempts );


//JA25UB
#ifdef JA2UB
void HandleQuestCodeOnSectorExit( INT16 sOldSectorX, INT16 sOldSectorY, INT8 bOldSectorZ );
void HandlePotentialMoraleHitForSkimmingSectors( GROUP *pGroup );
void HandlePlayerTeamQuotesWhenEnteringSector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ );
void ShouldNpcBeAddedToSector( INT16 sMapX, INT16 sMapY, INT8 bMapZ );
void HandlePlayerQuotesWhenEnteringFirstTunnelSector();
void AddExitGridForFanToPowerGenSector();
void HandleSectorSpecificUnLoadingOfMap( INT16 sMapX, INT16 sMapY, INT8 bMapZ );
//void MakeAllTeamMembersCrouchedThenStand();
void HandleMovingTheEnemiesToBeNearPlayerWhenEnteringComplexMap();
void HandleFortifiedDoor();
void CreateAndAddMoneyObjectToGround( UINT32 sGridNo, INT32 iEasyAmount, INT32 iNormalAmount, INT32 iHardAmount );
void HandleGoingUpOrDownStairsForLoadScreensPurposes( INT16 sCurrentlyInSectorZ, INT16 sGoingToSectorZ );
void HandleMovingEnemiesCloseToEntranceInFirstTunnelMap();
void HandleMovingEnemiesCloseToEntranceInSecondTunnelMap();
void HandleFirstPartOfTunnelFanSound();
void HandlePowerGenFanSoundModification();
BOOLEAN MoveEnemyFromGridNoToRoofGridNo( UINT32 sSourceGridNo, UINT32 sDestGridNo );
void		HandleMovingEnemiesOntoRoofs();
#endif

void HandleQuestCodeOnSectorExit( INT16 sOldSectorX, INT16 sOldSectorY, INT8 bOldSectorZ );
void HandlePotentialMoraleHitForSkimmingSectors( GROUP *pGroup );


extern void InitializeTacticalStatusAtBattleStart();

extern HVSURFACE ghFrameBuffer;
extern BOOLEAN gfOverrideSector;

extern STR16 pBullseyeStrings[];

extern void HandleRPCDescription( );

#ifdef CRIPPLED_VERSION
void CrippledVersionFailureToLoadMapCallBack( UINT8 bExitValue );
void CrippledVersionFailureToLoadMapCheck();
#endif


//temp timer stuff -- to measure the time it takes to load a map.
#ifdef JA2TESTVERSION
	extern INT16	 gsAINumAdmins;
	extern INT16	 gsAINumTroops;
	extern INT16	 gsAINumElites;
	extern INT16	 gsAINumCreatures;
	//The wrapper time for EnterSector
	BOOLEAN fStartNewFile = TRUE;
	UINT32 uiEnterSectorStartTime;
	UINT32 uiEnterSectorEndTime;
	//The grand total time for loading a map
	UINT32 uiLoadWorldStartTime;
	UINT32 uiLoadWorldEndTime;
	//The time spent in FileRead
	UINT32 uiTotalFileReadTime;
	UINT32 uiTotalFileReadCalls;
	//LoadWorld and parts
	UINT32 uiLoadWorldTime;
	UINT32 uiTrashWorldTime;
	UINT32 uiLoadMapTilesetTime;
	UINT32 uiLoadMapLightsTime;
	UINT32 uiBuildShadeTableTime;
	UINT32 uiNumTablesSaved;
	UINT32 uiNumTablesLoaded;
	UINT32 uiNumImagesReloaded;
	#include "render dirty.h"
	#include "tiledat.h"
#endif

// SAM externalization stuff

#define MAX_CHAR_DATA_LENGTH			500
#define INVALID_SAMSITE_INDEX			-1

extern INT16 gpSamSectorX[ MAX_NUMBER_OF_SAMS ];
extern INT16 gpSamSectorY[ MAX_NUMBER_OF_SAMS ];

typedef enum
{
	SAMSITE_ELEMENT_NONE = 0,
	SAMSITE_ELEMENT_SAMINFO,
	SAMSITE_ELEMENT_SAMLIST,
	SAMSITE_ELEMENT_SAM_CONTROLLED_SECTORS,
	SAMSITE_ELEMENT_MAP_ROW,
	SAMSITE_ELEMENT_SAM,
	SAMSITE_ELEMENT_INDEX,
	SAMSITE_ELEMENT_SAMSECTOR,
	SAMSITE_ELEMENT_SAMSECTOR_X,
	SAMSITE_ELEMENT_SAMSECTOR_Y,
	SAMSITE_ELEMENT_SAM_HIDDEN,
	SAMSITE_ELEMENT_SAM_ORIENTATION,
	SAMSITE_ELEMENT_SAM_GRIDNO_A,
	SAMSITE_ELEMENT_SAM_GRIDNO_B
} SAMSITE_PARSE_STAGE;

typedef struct
{
	UINT32	uiIndex;
	INT16	samSectorX;
	INT16	samSectorY;
	BOOLEAN samHidden;
	INT8	samOrientation;
	INT32	samGridNoA;
	INT32	samGridNoB;
} samInfo;

typedef struct
{
	SAMSITE_PARSE_STAGE	curElement;

	CHAR8					szCharData[MAX_CHAR_DATA_LENGTH+1];
	samInfo					curSamInfo;
	UINT32					uiRowNumber;
	UINT32					uiHighestIndex;

	UINT32					currentDepth;
	UINT32					maxReadDepth;
} samsiteParseData;

static void XMLCALL
samsiteStartElementHandle(void *userData, const XML_Char *name, const XML_Char **atts)
{
	samsiteParseData * pData = (samsiteParseData *) userData;

	if(pData->currentDepth <= pData->maxReadDepth) //are we reading this element?
	{

		if(strcmp(name, "SAM_INFO") == 0 && pData->curElement == SAMSITE_ELEMENT_NONE)
		{
			pData->curElement = SAMSITE_ELEMENT_SAMINFO;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "SAM_CONTROLLED_SECTORS") == 0 && pData->curElement == SAMSITE_ELEMENT_SAMINFO)
		{
			pData->curElement = SAMSITE_ELEMENT_SAM_CONTROLLED_SECTORS;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "MAP_ROW") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM_CONTROLLED_SECTORS)
		{
			UINT32 uiAttrIndex;
			pData->curElement = SAMSITE_ELEMENT_MAP_ROW;

			for(uiAttrIndex = 0;atts[uiAttrIndex] != NULL;uiAttrIndex += 2)
				if(strcmp(atts[uiAttrIndex], "row") == 0)
				{
					pData->uiRowNumber = atol(atts[uiAttrIndex+1]);
					break;
				}

			if(atts[uiAttrIndex] != NULL && pData->uiRowNumber < MAP_WORLD_Y)
				pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "SAMLIST") == 0 && pData->curElement == SAMSITE_ELEMENT_SAMINFO)
		{
			pData->curElement = SAMSITE_ELEMENT_SAMLIST;

			memset( fSamSiteFoundOrig,	0,	sizeof(fSamSiteFoundOrig)	);
			memset( pSamList,			0,	sizeof(pSamList)			);
			memset( pSamGridNoAList,	0,	sizeof(pSamGridNoAList)		);
			memset( pSamGridNoBList,	0,	sizeof(pSamGridNoBList)		);
			memset( gbSAMGraphicList,	0,	sizeof(gbSAMGraphicList)	);
			memset( gpSamSectorX,		0,	sizeof(gpSamSectorX)		);
			memset( gpSamSectorY,		0,	sizeof(gpSamSectorY)		);

			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "SAM") == 0 && pData->curElement == SAMSITE_ELEMENT_SAMLIST)
		{
			pData->curElement = SAMSITE_ELEMENT_SAM;

			memset( &pData->curSamInfo, 0, sizeof(samInfo) );

			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "samIndex") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM)
		{
			pData->curElement = SAMSITE_ELEMENT_INDEX;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "samSector") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM)
		{
			pData->curElement = SAMSITE_ELEMENT_SAMSECTOR;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "x") == 0 && pData->curElement == SAMSITE_ELEMENT_SAMSECTOR)
		{
			pData->curElement = SAMSITE_ELEMENT_SAMSECTOR_X;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "y") == 0 && pData->curElement == SAMSITE_ELEMENT_SAMSECTOR)
		{
			pData->curElement = SAMSITE_ELEMENT_SAMSECTOR_Y;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "samHidden") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM)
		{
			pData->curElement = SAMSITE_ELEMENT_SAM_HIDDEN;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "samOrientation") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM)
		{
			pData->curElement = SAMSITE_ELEMENT_SAM_ORIENTATION;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "samGridNoA") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM)
		{
			pData->curElement = SAMSITE_ELEMENT_SAM_GRIDNO_A;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "samGridNoB") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM)
		{
			pData->curElement = SAMSITE_ELEMENT_SAM_GRIDNO_B;
			pData->maxReadDepth++; //we are not skipping this element
		}
		pData->szCharData[0] = '\0';
	}

	pData->currentDepth++;

}

static void XMLCALL
samsiteCharacterDataHandle(void *userData, const XML_Char *str, int len)
{
	samsiteParseData * pData = (samsiteParseData *) userData;

	if(pData->currentDepth <= pData->maxReadDepth && strlen(pData->szCharData) < MAX_CHAR_DATA_LENGTH)
		strncat(pData->szCharData,str,__min((unsigned int)len,MAX_CHAR_DATA_LENGTH-strlen(pData->szCharData)));
}


static void XMLCALL
samsiteEndElementHandle(void *userData, const XML_Char *name)
{
	samsiteParseData * pData = (samsiteParseData *) userData;

	if(pData->currentDepth <= pData->maxReadDepth) //we're at the end of an element that we've been reading
	{
		if(strcmp(name, "SAM_INFO") == 0 && pData->curElement == SAMSITE_ELEMENT_SAMINFO)
		{
			pData->curElement = SAMSITE_ELEMENT_NONE;
		}
		else if(strcmp(name, "SAM_CONTROLLED_SECTORS") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM_CONTROLLED_SECTORS)
		{
			pData->curElement = SAMSITE_ELEMENT_SAMINFO;
		}
		else if(strcmp(name, "MAP_ROW") == 0 && pData->curElement == SAMSITE_ELEMENT_MAP_ROW)
		{
			STR8 curBuffer = pData->szCharData + strspn(pData->szCharData," \t\n\r");
			UINT32 curCellIndex = 0;
			UINT32 curNumber;

			pData->curElement = SAMSITE_ELEMENT_SAM_CONTROLLED_SECTORS;

			while(curBuffer[0] != '\0')
			{
				sscanf( curBuffer,"%d",&curNumber);

				ubSAMControlledSectors[pData->uiRowNumber][curCellIndex] = curNumber;

				curCellIndex++;
				curBuffer += strcspn(curBuffer," \t\n\r\0");
				curBuffer += strspn(curBuffer," \t\n\r");
			}
		}
		else if(strcmp(name, "SAMLIST") == 0 && pData->curElement == SAMSITE_ELEMENT_SAMLIST)
		{
			pData->curElement = SAMSITE_ELEMENT_SAMINFO;

			NUMBER_OF_SAMS = pData->uiHighestIndex;
		}
		else if(strcmp(name, "SAM") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM)
		{
			pData->curElement = SAMSITE_ELEMENT_SAMLIST;

			if ( pData->curSamInfo.uiIndex != INVALID_SAMSITE_INDEX )
			{
				pData->curSamInfo.uiIndex--;
				fSamSiteFoundOrig[ pData->curSamInfo.uiIndex ] = !pData->curSamInfo.samHidden;
				gpSamSectorX	 [ pData->curSamInfo.uiIndex ] = pData->curSamInfo.samSectorX;
				gpSamSectorY	 [ pData->curSamInfo.uiIndex ] = pData->curSamInfo.samSectorY;
				pSamList		 [ pData->curSamInfo.uiIndex ] = SECTOR(pData->curSamInfo.samSectorX,pData->curSamInfo.samSectorY);
				gbSAMGraphicList [ pData->curSamInfo.uiIndex ] = pData->curSamInfo.samOrientation;
				pSamGridNoAList	 [ pData->curSamInfo.uiIndex ] = pData->curSamInfo.samGridNoA;
				pSamGridNoBList	 [ pData->curSamInfo.uiIndex ] = pData->curSamInfo.samGridNoB;
			}
		}
		else if(strcmp(name, "samIndex") == 0 && pData->curElement == SAMSITE_ELEMENT_INDEX)
		{
			pData->curElement = SAMSITE_ELEMENT_SAM;

			pData->curSamInfo.uiIndex = atol(pData->szCharData);
			if ( !pData->curSamInfo.uiIndex || pData->curSamInfo.uiIndex > MAX_NUMBER_OF_SAMS )
			{
				pData->curSamInfo.uiIndex = INVALID_SAMSITE_INDEX;
			}
			else if ( pData->curSamInfo.uiIndex > pData->uiHighestIndex )
			{
				pData->uiHighestIndex = pData->curSamInfo.uiIndex;
			}
		}
		else if(strcmp(name, "samSector") == 0 && pData->curElement == SAMSITE_ELEMENT_SAMSECTOR)
		{
			pData->curElement = SAMSITE_ELEMENT_SAM;
		}
		else if(strcmp(name, "x") == 0 && pData->curElement == SAMSITE_ELEMENT_SAMSECTOR_X)
		{
			pData->curElement = SAMSITE_ELEMENT_SAMSECTOR;

			pData->curSamInfo.samSectorX = (INT16) atol(pData->szCharData);
		}
		else if(strcmp(name, "y") == 0 && pData->curElement == SAMSITE_ELEMENT_SAMSECTOR_Y)
		{
			pData->curElement = SAMSITE_ELEMENT_SAMSECTOR;

			pData->curSamInfo.samSectorY = (INT16) atol(pData->szCharData);
		}
		else if(strcmp(name, "samHidden") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM_HIDDEN )
		{
			pData->curElement = SAMSITE_ELEMENT_SAM;

			pData->curSamInfo.samHidden = (BOOLEAN)atol(pData->szCharData);
		}
		else if(strcmp(name, "samOrientation") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM_ORIENTATION )
		{
			pData->curElement = SAMSITE_ELEMENT_SAM;

			pData->curSamInfo.samOrientation = (INT8)atol(pData->szCharData);
			if ( pData->curSamInfo.samOrientation != 3 || pData->curSamInfo.samOrientation != 4 )
			{
				pData->curSamInfo.samOrientation = 3;
			}
		}
		else if(strcmp(name, "samGridNoA") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM_GRIDNO_A )
		{
			pData->curElement = SAMSITE_ELEMENT_SAM;

			pData->curSamInfo.samGridNoA = atol(pData->szCharData);
			if ( pData->curSamInfo.samGridNoA >= WORLD_MAX )
			{
				pData->curSamInfo.samGridNoA = 0;
			}
		}
		else if(strcmp(name, "samGridNoB") == 0 && pData->curElement == SAMSITE_ELEMENT_SAM_GRIDNO_B )
		{
			pData->curElement = SAMSITE_ELEMENT_SAM;

			pData->curSamInfo.samGridNoB = atol(pData->szCharData);
			if ( pData->curSamInfo.samGridNoB >= WORLD_MAX )
			{
				pData->curSamInfo.samGridNoB = 1;
			}
		}
		pData->maxReadDepth--;
	}

	pData->currentDepth--;
}

BOOLEAN ReadInSAMInfo(STR fileName)
{
	HWFILE		hFile;
	UINT32		uiBytesRead;
	UINT32		uiFSize;
	CHAR8 *		lpcBuffer;
	XML_Parser	parser = XML_ParserCreate(NULL);

	samsiteParseData pData;



	// Open weapons file
	hFile = FileOpen( fileName, FILE_ACCESS_READ, FALSE );
	if ( !hFile )
		return( FALSE );

	uiFSize = FileGetSize(hFile);
	lpcBuffer = (CHAR8 *) MemAlloc(uiFSize+1);

	//Read in block
	if ( !FileRead( hFile, lpcBuffer, uiFSize, &uiBytesRead ) )
	{
		MemFree(lpcBuffer);
		return( FALSE );
	}

	lpcBuffer[uiFSize] = 0; //add a null terminator

	FileClose( hFile );


	XML_SetElementHandler(parser, samsiteStartElementHandle, samsiteEndElementHandle);
	XML_SetCharacterDataHandler(parser, samsiteCharacterDataHandle);


	memset(&pData,0,sizeof(pData));
	NUMBER_OF_SAMS = 0;
	XML_SetUserData(parser, &pData);


    if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE))
	{
		CHAR8 errorBuf[511];

		sprintf(errorBuf, "XML Parser Error in SamSites.xml: %s at line %d", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser));
		LiveMessage(errorBuf);

		MemFree(lpcBuffer);
		return FALSE;
	}

	MemFree(lpcBuffer);

	XML_ParserFree(parser);

	return TRUE;
}

BOOLEAN WriteInSAMInfo(STR fileName)
{
	HWFILE		hFile;

	hFile = FileOpen( fileName, FILE_ACCESS_WRITE | FILE_CREATE_ALWAYS, FALSE );
	if ( !hFile )
		return( FALSE );

	{
		UINT32 x, y;
		INT8 cnt;


		FilePrintf(hFile,"<SAM_INFO>\r\n");
		FilePrintf(hFile,"\t<SAMLIST>\r\n");
		for(cnt = 0; cnt < NUMBER_OF_SAMS; cnt++)
		{
			FilePrintf(hFile,"\t\t<SAM>\r\n");

			FilePrintf(hFile,"\t\t\t<samIndex>%d</samIndex>\r\n",cnt+1);

			FilePrintf(hFile,"\t\t\t<samSector>\r\n");
			FilePrintf(hFile,"\t\t\t\t<x>%d</x>\r\n",(pSamList[cnt]%16)+1);
			FilePrintf(hFile,"\t\t\t\t<y>%d</y>\r\n",(pSamList[cnt]/16)+1);
			FilePrintf(hFile,"\t\t\t</samSector>\r\n");

			FilePrintf(hFile,"\t\t\t<samHidden>%d</samHidden>\r\n", !fSamSiteFound[cnt] );

			FilePrintf(hFile,"\t\t\t<samOrientation>%d</samOrientation>\r\n", gbSAMGraphicList[cnt] );

			FilePrintf(hFile,"\t\t\t<samGridNoA>%d</samGridNoA>\r\n", pSamGridNoAList[cnt] );

			FilePrintf(hFile,"\t\t\t<samGridNoB>%d</samGridNoB>\r\n", pSamGridNoBList[cnt] );

			FilePrintf(hFile,"\t\t</SAM>\r\n");
		}
		FilePrintf(hFile,"\t</SAMLIST>\r\n");

		FilePrintf(hFile,"\t<SAM_CONTROLLED_SECTORS>\r\n");
		for(y = 0;y < MAP_WORLD_Y;y++)
		{
			FilePrintf(hFile,"\t\t<MAP_ROW row=\"%02d\">",y);
			for(x = 0;x < MAP_WORLD_X;x++)
			{
				if(x < MAP_WORLD_X-1)
				{
					FilePrintf(hFile,"%2d ",ubSAMControlledSectors[y][x]);
				}
				else
					FilePrintf(hFile,"%2d </MAP_ROW>\r\n",ubSAMControlledSectors[y][x]);
			}
		}
		FilePrintf(hFile,"\t</SAM_CONTROLLED_SECTORS>\r\n");
		FilePrintf(hFile,"</SAM_INFO>\r\n");
	}
	FileClose( hFile );

	return TRUE;
}


// town externalization stuff
INT8 NUM_TOWNS;

extern UINT8	gubTownRebelSentiment	[ MAX_TOWNS ];
extern BOOLEAN	gfTownUsesLoyalty		[ MAX_TOWNS ];
extern BOOLEAN	gfMilitiaAllowedInTown	[ MAX_TOWNS ];
BOOLEAN	gfHiddenTown			[ MAX_TOWNS ];				// Info: Visible town are TRUE, hidden towns are FALSE
BOOLEAN	gfDrawHiddenTown		[ MAX_TOWNS ];
BOOLEAN	gfDrawHiddenTownTemp	[ MAX_TOWNS ];
BOOLEAN	gfHiddenTownTemp		[ MAX_TOWNS ];
BOOLEAN	gfIconTown				[ MAX_TOWNS ];
BOOLEAN	gfIconTownTemp			[ MAX_TOWNS ];

#define INVALID_TOWN_INDEX				-1

typedef enum
{
	CITYTABLE_ELEMENT_NONE = 0,
	CITYTABLE_ELEMENT_CITYINFO,
	CITYTABLE_ELEMENT_CITYTABLE,
	CITYTABLE_ELEMENT_CITYTABLEROW,
	CITYTABLE_ELEMENT_CITYLIST,
	CITYTABLE_ELEMENT_CITY,
	CITYTABLE_ELEMENT_INDEX,
	CITYTABLE_ELEMENT_NAME,
	CITYTABLE_ELEMENT_BASESECTOR,
	CITYTABLE_ELEMENT_BASESECTOR_X,
	CITYTABLE_ELEMENT_BASESECTOR_Y,
	CITYTABLE_ELEMENT_TOWNPOINT,
	CITYTABLE_ELEMENT_TOWNPOINT_X,
	CITYTABLE_ELEMENT_TOWNPOINT_Y,
	CITYTABLE_ELEMENT_USES_LOYALTY,
	CITYTABLE_ELEMENT_REBEL_SENTIMENT,
	CITYTABLE_ELEMENT_MILITIA,
	CITYTABLE_ELEMENT_HIDDENTOWN,
	CITYTABLE_ELEMENT_ICONTOWN,
	CITYTABLE_ELEMENT_ICONFILE,
	CITYTABLE_ELEMENT_ICON_POSITION,
	CITYTABLE_ELEMENT_ICON_POSITION_X,
	CITYTABLE_ELEMENT_ICON_POSITION_Y
	
} CITYTABLE_PARSE_STAGE;

typedef struct
{
	UINT32	uiIndex;
	UINT8	ubBaseX;
	UINT8	ubBaseY;
	POINT	townPoint;
	BOOLEAN townUsesLoyalty;
	UINT8	townRebelSentiment;
	BOOLEAN	townMilitiaAllowed;
	CHAR8	cityName[MAX_TOWN_NAME_LENGHT];
	BOOLEAN HiddenTown;
	BOOLEAN	TownIcon;
	CHAR8	IconSTI[MAX_ICON_CHARS];
	UINT8	ubPosIconX;
	UINT8	ubPosIconY;
} cityInfo;

typedef struct
{
	CITYTABLE_PARSE_STAGE	curElement;

	CHAR8					szCharData[MAX_CHAR_DATA_LENGTH+1];
	cityInfo				curCityInfo;
	
	//cityInfo *				curArray;	// ROMAN: added
	//UINT32					maxArraySize;	// ROMAN: added

	UINT32					uiRowNumber;
	UINT32					uiHighestIndex;

	UINT32					currentDepth;
	UINT32					maxReadDepth;
} citytableParseData;

static void XMLCALL
citytableStartElementHandle(void *userData, const XML_Char *name, const XML_Char **atts)
{
	citytableParseData * pData = (citytableParseData *) userData;

	memset( pData->szCharData, 0, sizeof( pData->szCharData) );

	if(pData->currentDepth <= pData->maxReadDepth) //are we reading this element?
	{

		if(strcmp(name, "CITY_INFO") == 0 && pData->curElement == CITYTABLE_ELEMENT_NONE)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITYINFO;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "CITY_TABLE") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITYINFO)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITYTABLE;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "CITY_TABLE_ROW") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITYTABLE)
		{
			UINT32 uiAttrIndex;
			pData->curElement = CITYTABLE_ELEMENT_CITYTABLEROW;

			for(uiAttrIndex = 0;atts[uiAttrIndex] != NULL;uiAttrIndex += 2)
				if(strcmp(atts[uiAttrIndex], "row") == 0)
				{
					pData->uiRowNumber = atol(atts[uiAttrIndex+1]);
					break;
				}

			if(atts[uiAttrIndex] != NULL && pData->uiRowNumber < MAP_WORLD_Y)
				pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "CITYLIST") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITYINFO)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITYLIST;

			if ( !localizedMapTextOnly )
			{
				memset(sBaseSectorList,0,sizeof(sBaseSectorList));
				memset(pTownPoints,0,sizeof(pTownPoints));
				memset(gfTownUsesLoyalty,0,sizeof(gfTownUsesLoyalty));
				memset(gubTownRebelSentiment,0,sizeof(gubTownRebelSentiment));
				memset(gfMilitiaAllowedInTown,0,sizeof(gfMilitiaAllowedInTown));
				memset(gfHiddenTown,0,sizeof(gfHiddenTown));
				memset(gfHiddenTownTemp,0,sizeof(gfHiddenTownTemp));
				memset(gfIconTown,0,sizeof(gfIconTown));
				memset(gfIconTownTemp,0,sizeof(gfIconTownTemp));
				memset(gfDrawHiddenTown,0,sizeof(gfDrawHiddenTown));
				memset(gfDrawHiddenTownTemp,0,sizeof(gfDrawHiddenTownTemp));
				
				strncpy(gHiddenIcon[pData->curCityInfo.uiIndex].IconSti, pData->curCityInfo.IconSTI,MAX_ICON_CHARS);
				
				//if ( pData->curCityInfo.ubPosIconX < 0 )
				//	gHiddenIcon[pData->curCityInfo.uiIndex].IconX = 0;
				//else
					gHiddenIcon[pData->curCityInfo.uiIndex].IconX = pData->curCityInfo.ubPosIconX;
				
				//if ( pData->curCityInfo.ubPosIconY < 0 )
				//	gHiddenIcon[pData->curCityInfo.uiIndex].IconY = 0;
				//else
					gHiddenIcon[pData->curCityInfo.uiIndex].IconY = pData->curCityInfo.ubPosIconY;
					
			}

			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "CITY") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITYLIST)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITY;

			if ( !localizedMapTextOnly )
				memset(&pData->curCityInfo,0,sizeof(cityInfo));

			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "townName") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITY)
		{
			pData->curElement = CITYTABLE_ELEMENT_NAME;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "uiIndex") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITY)
		{
			pData->curElement = CITYTABLE_ELEMENT_INDEX;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "townUsesLoyalty") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITY)
		{
			pData->curElement = CITYTABLE_ELEMENT_USES_LOYALTY;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "townRebelSentiment") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITY)
		{
			pData->curElement = CITYTABLE_ELEMENT_REBEL_SENTIMENT;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "townMilitiaAllowed") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITY)
		{
			pData->curElement = CITYTABLE_ELEMENT_MILITIA;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "hiddenTown") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITY)
		{
			pData->curElement = CITYTABLE_ELEMENT_HIDDENTOWN;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "townIcon") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITY)
		{
			pData->curElement = CITYTABLE_ELEMENT_ICONTOWN;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "szIconFile") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITY)
		{
			pData->curElement = CITYTABLE_ELEMENT_ICONFILE;
			pData->maxReadDepth++; //we are not skipping this element
		}
		
		else if(strcmp(name, "iconPosition") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITY)
		{
			pData->curElement = CITYTABLE_ELEMENT_ICON_POSITION;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "x") == 0 && pData->curElement == CITYTABLE_ELEMENT_ICON_POSITION)
		{
			pData->curElement = CITYTABLE_ELEMENT_ICON_POSITION_X;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "y") == 0 && pData->curElement == CITYTABLE_ELEMENT_ICON_POSITION)
		{
			pData->curElement = CITYTABLE_ELEMENT_ICON_POSITION_Y;
			pData->maxReadDepth++; //we are not skipping this element
		}
		
		else if(strcmp(name, "baseSector") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITY)
		{
			pData->curElement = CITYTABLE_ELEMENT_BASESECTOR;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "x") == 0 && pData->curElement == CITYTABLE_ELEMENT_BASESECTOR)
		{
			pData->curElement = CITYTABLE_ELEMENT_BASESECTOR_X;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "y") == 0 && pData->curElement == CITYTABLE_ELEMENT_BASESECTOR)
		{
			pData->curElement = CITYTABLE_ELEMENT_BASESECTOR_Y;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "townPoint") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITY)
		{
			pData->curElement = CITYTABLE_ELEMENT_TOWNPOINT;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "x") == 0 && pData->curElement == CITYTABLE_ELEMENT_TOWNPOINT)
		{
			pData->curElement = CITYTABLE_ELEMENT_TOWNPOINT_X;
			pData->maxReadDepth++; //we are not skipping this element
		}
		else if(strcmp(name, "y") == 0 && pData->curElement == CITYTABLE_ELEMENT_TOWNPOINT)
		{
			pData->curElement = CITYTABLE_ELEMENT_TOWNPOINT_Y;
			pData->maxReadDepth++; //we are not skipping this element
		}

		//pData->szCharData[0] = '\0';
	}

	pData->currentDepth++;

}

static void XMLCALL
citytableCharacterDataHandle(void *userData, const XML_Char *str, int len)
{
	citytableParseData * pData = (citytableParseData *) userData;

	if(pData->currentDepth <= pData->maxReadDepth && strlen(pData->szCharData) < MAX_CHAR_DATA_LENGTH)
		strncat(pData->szCharData,str,__min((unsigned int)len,MAX_CHAR_DATA_LENGTH-strlen(pData->szCharData)));
}


static void XMLCALL
citytableEndElementHandle(void *userData, const XML_Char *name)
{

char temp;

	citytableParseData * pData = (citytableParseData *) userData;

	if(pData->currentDepth <= pData->maxReadDepth) //we're at the end of an element that we've been reading
	{
		if(strcmp(name, "CITY_INFO") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITYINFO)
		{
			pData->curElement = CITYTABLE_ELEMENT_NONE;
		}
		else if(strcmp(name, "CITY_TABLE") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITYTABLE)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITYINFO;
		}
		else if(strcmp(name, "CITY_TABLE_ROW") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITYTABLEROW)
		{
			STR8 curBuffer = pData->szCharData + strspn(pData->szCharData," \t\n\r");
			UINT32 curCellIndex = 0;
			UINT32 curNumber;

			pData->curElement = CITYTABLE_ELEMENT_CITYTABLE;

			while(curBuffer[0] != '\0')
			{
				sscanf( curBuffer,"%d",&curNumber);

				StrategicMap[curCellIndex+MAP_WORLD_X*pData->uiRowNumber].bNameId = curNumber;

				curCellIndex++;
				curBuffer += strcspn(curBuffer," \t\n\r\0");
				curBuffer += strspn(curBuffer," \t\n\r");
			}
		}
		else if(strcmp(name, "CITYLIST") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITYLIST)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITYINFO;

			NUM_TOWNS = pData->uiHighestIndex + 1;
		}
		else if(strcmp(name, "CITY") == 0 && pData->curElement == CITYTABLE_ELEMENT_CITY)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITYLIST;

			if ( pData->curCityInfo.uiIndex != INVALID_TOWN_INDEX && !localizedMapTextOnly)
			{
				sBaseSectorList       [pData->curCityInfo.uiIndex-1] = SECTOR(pData->curCityInfo.ubBaseX,pData->curCityInfo.ubBaseY);
				pTownPoints           [pData->curCityInfo.uiIndex  ] = pData->curCityInfo.townPoint;
				gfTownUsesLoyalty     [pData->curCityInfo.uiIndex  ] = pData->curCityInfo.townUsesLoyalty;
				gubTownRebelSentiment [pData->curCityInfo.uiIndex  ] = pData->curCityInfo.townRebelSentiment;
				gfMilitiaAllowedInTown[pData->curCityInfo.uiIndex  ] = pData->curCityInfo.townMilitiaAllowed;
				//mbstowcs( pTownNames[pData->curCityInfo.uiIndex], pData->curCityInfo.cityName, MAX_TOWN_NAME_LENGHT);
				MultiByteToWideChar( CP_UTF8, 0, pData->curCityInfo.cityName, -1, pTownNames[pData->curCityInfo.uiIndex], MAX_TOWN_NAME_LENGHT);
				
				strncpy(gHiddenIcon[pData->curCityInfo.uiIndex].IconSti, pData->curCityInfo.IconSTI,MAX_ICON_CHARS);
				
				//if ( pData->curCityInfo.ubPosIconX < 0 )
				//	gHiddenIcon[pData->curCityInfo.uiIndex].IconX = 0;
				//else
					gHiddenIcon[pData->curCityInfo.uiIndex].IconX = pData->curCityInfo.ubPosIconX;
				
				//if ( pData->curCityInfo.ubPosIconY < 0 )
				//	gHiddenIcon[pData->curCityInfo.uiIndex].IconY = 0;
				//else
					gHiddenIcon[pData->curCityInfo.uiIndex].IconY = pData->curCityInfo.ubPosIconY;
				
				if ( pData->curCityInfo.HiddenTown == FALSE ) 
				{
					gfHiddenTown[pData->curCityInfo.uiIndex] = TRUE;
					gfHiddenTownTemp[pData->curCityInfo.uiIndex] = gfHiddenTown[pData->curCityInfo.uiIndex];
					gfDrawHiddenTown[pData->curCityInfo.uiIndex] = FALSE;	
				}
				else 
				{
					gfHiddenTown[pData->curCityInfo.uiIndex] = FALSE;
					gfHiddenTownTemp[pData->curCityInfo.uiIndex] = gfHiddenTown[pData->curCityInfo.uiIndex];
					gfDrawHiddenTown[pData->curCityInfo.uiIndex] = FALSE;
				}

				gfIconTown[pData->curCityInfo.uiIndex] = pData->curCityInfo.TownIcon;
				gfIconTownTemp[pData->curCityInfo.uiIndex] = gfIconTown[pData->curCityInfo.uiIndex];
			}
			else if ( pData->curCityInfo.uiIndex != INVALID_TOWN_INDEX && localizedMapTextOnly)
			{
				MultiByteToWideChar( CP_UTF8, 0, pData->curCityInfo.cityName, -1, pTownNames[pData->curCityInfo.uiIndex], MAX_TOWN_NAME_LENGHT);
			}
		}
		else if(strcmp(name, "uiIndex") == 0 && pData->curElement == CITYTABLE_ELEMENT_INDEX)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITY;

			pData->curCityInfo.uiIndex = atol(pData->szCharData);
			if ( !pData->curCityInfo.uiIndex || pData->curCityInfo.uiIndex >= MAX_TOWNS )
			{
				pData->curCityInfo.uiIndex = INVALID_TOWN_INDEX;
			}
			else if ( pData->curCityInfo.uiIndex > pData->uiHighestIndex )
			{
				pData->uiHighestIndex = pData->curCityInfo.uiIndex;
			}
		}
		else if(strcmp(name, "townName") == 0 && pData->curElement == CITYTABLE_ELEMENT_NAME)
		{
			strncpy(pData->curCityInfo.cityName, pData->szCharData, MAX_TOWN_NAME_LENGHT - 1);
			pData->curCityInfo.cityName[MAX_TOWN_NAME_LENGHT - 1] = 0;

			pData->curElement = CITYTABLE_ELEMENT_CITY;
		}
		else if(strcmp(name, "townUsesLoyalty") == 0 && pData->curElement == CITYTABLE_ELEMENT_USES_LOYALTY )
		{
			pData->curElement = CITYTABLE_ELEMENT_CITY;

			pData->curCityInfo.townUsesLoyalty = (BOOLEAN)atol(pData->szCharData);
		}
		else if(strcmp(name, "townRebelSentiment") == 0 && pData->curElement == CITYTABLE_ELEMENT_REBEL_SENTIMENT )
		{
			pData->curElement = CITYTABLE_ELEMENT_CITY;

			pData->curCityInfo.townRebelSentiment = (UINT8)atol(pData->szCharData);
			if ( pData->curCityInfo.townRebelSentiment > 100 )
			{
				pData->curCityInfo.townRebelSentiment = 100;
			}
			else if ( !pData->curCityInfo.townRebelSentiment )
				pData->curCityInfo.townRebelSentiment = 1;
		}
		else if(strcmp(name, "townMilitiaAllowed") == 0 && pData->curElement == CITYTABLE_ELEMENT_MILITIA )
		{
			pData->curElement = CITYTABLE_ELEMENT_CITY;

			pData->curCityInfo.townMilitiaAllowed = (BOOLEAN)atol(pData->szCharData);
		}
		else if(strcmp(name, "hiddenTown") == 0 && pData->curElement == CITYTABLE_ELEMENT_HIDDENTOWN)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITY;
			
			pData->curCityInfo.HiddenTown = (BOOLEAN)atol(pData->szCharData);
		}		
		else if(strcmp(name, "townIcon") == 0 && pData->curElement == CITYTABLE_ELEMENT_ICONTOWN)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITY;
			
			pData->curCityInfo.TownIcon = (INT32)atol(pData->szCharData);
		}
		else if(strcmp(name, "szIconFile") == 0 && pData->curElement == CITYTABLE_ELEMENT_ICONFILE)
		{
		
			pData->curElement = CITYTABLE_ELEMENT_CITY;
			
			if(MAX_ICON_CHARS >= strlen(pData->szCharData))
				strcpy(pData->curCityInfo.IconSTI,pData->szCharData);
			else
			{
				strncpy(pData->curCityInfo.IconSTI,pData->szCharData,MAX_ICON_CHARS);
				pData->curCityInfo.IconSTI[MAX_ICON_CHARS] = '\0';
			}

			for(int i=0;i<min((int)strlen(pData->szCharData),MAX_ICON_CHARS);i++)
			{
				temp = pData->szCharData[i];
				pData->curCityInfo.IconSTI[i] = temp;
			}
		}
		
		else if(strcmp(name, "iconPosition") == 0 && pData->curElement == CITYTABLE_ELEMENT_ICON_POSITION)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITY;
		}
		else if(strcmp(name, "x") == 0 && pData->curElement == CITYTABLE_ELEMENT_ICON_POSITION_X)
		{
			pData->curElement = CITYTABLE_ELEMENT_ICON_POSITION;

			pData->curCityInfo.ubPosIconX = (INT32) atol(pData->szCharData);
		}
		else if(strcmp(name, "y") == 0 && pData->curElement == CITYTABLE_ELEMENT_ICON_POSITION_Y)
		{
			pData->curElement = CITYTABLE_ELEMENT_ICON_POSITION;

			pData->curCityInfo.ubPosIconY = (INT32) atol(pData->szCharData);
		}
		
		else if(strcmp(name, "baseSector") == 0 && pData->curElement == CITYTABLE_ELEMENT_BASESECTOR)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITY;
		}
		else if(strcmp(name, "x") == 0 && pData->curElement == CITYTABLE_ELEMENT_BASESECTOR_X)
		{
			pData->curElement = CITYTABLE_ELEMENT_BASESECTOR;

			pData->curCityInfo.ubBaseX = (UINT8) atol(pData->szCharData);
		}
		else if(strcmp(name, "y") == 0 && pData->curElement == CITYTABLE_ELEMENT_BASESECTOR_Y)
		{
			pData->curElement = CITYTABLE_ELEMENT_BASESECTOR;

			pData->curCityInfo.ubBaseY = (UINT8) atol(pData->szCharData);
		}
		else if(strcmp(name, "townPoint") == 0 && pData->curElement == CITYTABLE_ELEMENT_TOWNPOINT)
		{
			pData->curElement = CITYTABLE_ELEMENT_CITY;
		}
		else if(strcmp(name, "x") == 0 && pData->curElement == CITYTABLE_ELEMENT_TOWNPOINT_X)
		{
			pData->curElement = CITYTABLE_ELEMENT_TOWNPOINT;

			pData->curCityInfo.townPoint.x = atol(pData->szCharData);
		}
		else if(strcmp(name, "y") == 0 && pData->curElement == CITYTABLE_ELEMENT_TOWNPOINT_Y)
		{
			pData->curElement = CITYTABLE_ELEMENT_TOWNPOINT;

			pData->curCityInfo.townPoint.y = atol(pData->szCharData);
		}

		pData->maxReadDepth--;
	}

	pData->currentDepth--;
}

BOOLEAN WriteInStrategicMapSectorTownNames(STR fileName)
{
	HWFILE		hFile;

	hFile = FileOpen( fileName, FILE_ACCESS_WRITE | FILE_CREATE_ALWAYS, FALSE );
	if ( !hFile )
		return( FALSE );

	{
		UINT32 x, y;
		INT8 cnt;


		FilePrintf(hFile,"<CITY_INFO>\r\n");
		FilePrintf(hFile,"\t<CITYLIST>\r\n");
		for(cnt = 1; cnt < NUM_TOWNS; cnt++)
		{
			FilePrintf(hFile,"\t\t<CITY>\r\n");

			FilePrintf(hFile,"\t\t\t<uiIndex>%d</uiIndex>\r\n",cnt);

			FilePrintf(hFile,"\t\t\t<townName>%S</townName>\r\n", pTownNames[cnt] );

			FilePrintf(hFile,"\t\t\t<townUsesLoyalty>%d</townUsesLoyalty>\r\n", gfTownUsesLoyalty[cnt] );

			FilePrintf(hFile,"\t\t\t<townRebelSentiment>%d</townRebelSentiment>\r\n", gubTownRebelSentiment[cnt] );

			FilePrintf(hFile,"\t\t\t<townMilitiaAllowed>%d</townMilitiaAllowed>\r\n", gfMilitiaAllowedInTown[cnt] );
			
			if ( gfHiddenTown[cnt] == TRUE)
			FilePrintf(hFile,"\t\t\t<hiddenTown>%d</hiddenTown>\r\n", 0 );
			else
			FilePrintf(hFile,"\t\t\t<hiddenTown>%d</hiddenTown>\r\n", 1 );
			
			FilePrintf(hFile,"\t\t\t<townIcon>%d</townIcon>\r\n", gfIconTown[cnt] );

			FilePrintf(hFile,"\t\t\t<baseSector>\r\n");
			FilePrintf(hFile,"\t\t\t\t<x>%d</x>\r\n",(sBaseSectorList[cnt-1]%16)+1);
			FilePrintf(hFile,"\t\t\t\t<y>%d</y>\r\n",(sBaseSectorList[cnt-1]/16)+1);
			FilePrintf(hFile,"\t\t\t</baseSector>\r\n");


			FilePrintf(hFile,"\t\t\t<townPoint>\r\n");
			FilePrintf(hFile,"\t\t\t\t<x>%d</x>\r\n",pTownPoints[cnt].x);
			FilePrintf(hFile,"\t\t\t\t<y>%d</y>\r\n",pTownPoints[cnt].y);
			FilePrintf(hFile,"\t\t\t</townPoint>\r\n");


			FilePrintf(hFile,"\t\t</CITY>\r\n");
		}
		FilePrintf(hFile,"\t</CITYLIST>\r\n");

		FilePrintf(hFile,"\t<CITY_TABLE>\r\n");
		for(y = 0;y < MAP_WORLD_Y;y++)
		{
			FilePrintf(hFile,"\t\t<CITY_TABLE_ROW row=\"%02d\">",y);
			for(x = 0;x < MAP_WORLD_X;x++)
			{
				if(x < MAP_WORLD_X-1)
				{
					FilePrintf(hFile,"%3d",StrategicMap[x+y*MAP_WORLD_X].bNameId);
				}
				else
					FilePrintf(hFile,"%3d </CITY_TABLE_ROW>\r\n",StrategicMap[x+y*MAP_WORLD_X].bNameId);
			}
		}
		FilePrintf(hFile,"\t</CITY_TABLE>\r\n");
		FilePrintf(hFile,"</CITY_INFO>\r\n");
	}
	FileClose( hFile );

	return TRUE;
}

BOOLEAN ReadInStrategicMapSectorTownNames(STR fileName, BOOLEAN localizedVersion)
{
	HWFILE		hFile;
	UINT32		uiBytesRead;
	UINT32		uiFSize;
	CHAR8 *		lpcBuffer;
	XML_Parser	parser = XML_ParserCreate(NULL);

	citytableParseData pData;

	localizedMapTextOnly = localizedVersion;

	// Open weapons file
	hFile = FileOpen( fileName, FILE_ACCESS_READ, FALSE );
	if ( !hFile )
		return( FALSE );

	uiFSize = FileGetSize(hFile);
	lpcBuffer = (CHAR8 *) MemAlloc(uiFSize+1);

	//Read in block
	if ( !FileRead( hFile, lpcBuffer, uiFSize, &uiBytesRead ) )
	{
		MemFree(lpcBuffer);
		return( FALSE );
	}

	lpcBuffer[uiFSize] = 0; //add a null terminator

	FileClose( hFile );


	XML_SetElementHandler(parser, citytableStartElementHandle, citytableEndElementHandle);
	XML_SetCharacterDataHandler(parser, citytableCharacterDataHandle);


	// TODO: ROMAN
	//pData.curCityInfo


	memset(&pData,0,sizeof(pData));
	NUM_TOWNS = 0;
	XML_SetUserData(parser, &pData);


    if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE))
	{
		CHAR8 errorBuf[511];

		sprintf(errorBuf, "XML Parser Error in Cities.xml: %s at line %d", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser));
		LiveMessage(errorBuf);

		MemFree(lpcBuffer);
		return FALSE;
	}

	MemFree(lpcBuffer);

	XML_ParserFree(parser);

	return TRUE;
}

BOOLEAN ReadInMapStructure(STR fileName, BOOLEAN localizedVersion)
{
	memset(StrategicMap, 0, sizeof(StrategicMap));

	return ReadInStrategicMapSectorTownNames(fileName,localizedVersion);
}

UINT32 UndergroundTacticalTraversalTime( INT8 bExitDirection )
{
	//We are attempting to traverse in an underground environment.  We need to use a complete different
	//method.  When underground, all sectors are instantly adjacent.
	switch( bExitDirection )
	{
		case NORTH_STRATEGIC_MOVE:
			if( gMapInformation.sNorthGridNo != -1 )
				return 0;
			break;
		case EAST_STRATEGIC_MOVE:
			if( gMapInformation.sEastGridNo != -1 )
				return 0;
			break;
		case SOUTH_STRATEGIC_MOVE:
			if( gMapInformation.sSouthGridNo != -1 )
				return 0;
			break;
		case WEST_STRATEGIC_MOVE:
			if( gMapInformation.sWestGridNo != -1 )
				return 0;
			break;
	}
	return 0xffffffff;
}

// WANNE: Zooming Animation of the laptop in strategic screen
void BeginLoadScreen( void )
{
	SGPRect SrcRect, DstRect;
	UINT32 uiStartTime, uiCurrTime;
	INT32 iPercentage, iFactor;
	UINT32 uiTimeRange;
	INT32 iLastShadePercentage;
	UINT8 ubLoadScreenID;

	SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR );

#ifdef JA2UB
	if( guiCurrentScreen == MAP_SCREEN && !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME) )
#else
//Ja25: No meanwhiles
	if( guiCurrentScreen == MAP_SCREEN && !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME) && !AreInMeanwhile() )
#endif
	{
		DstRect.iLeft = 0;
		DstRect.iTop = 0;
		DstRect.iRight = SCREEN_WIDTH;
		DstRect.iBottom = SCREEN_HEIGHT;
		uiTimeRange = 2000;
		iPercentage = 0;
		iLastShadePercentage = 0;
		uiStartTime = GetJA2Clock();

		BlitBufferToBuffer( FRAME_BUFFER, guiSAVEBUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
		PlayJA2SampleFromFile( "SOUNDS\\Final Psionic Blast 01 (16-44).wav", RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
		while( iPercentage < 100  )
		{
			uiCurrTime = GetJA2Clock();
			iPercentage = (uiCurrTime-uiStartTime) * 100 / uiTimeRange;
			iPercentage = min( iPercentage, 100 );

			//Factor the percentage so that it is modified by a gravity falling acceleration effect.
			iFactor = (iPercentage - 50) * 2;
			if( iPercentage < 50 )
				iPercentage = (UINT32)(iPercentage + iPercentage * iFactor * 0.01 + 0.5);
			else
				iPercentage = (UINT32)(iPercentage + (100-iPercentage) * iFactor * 0.01 + 0.05);

			if( iPercentage > 50 )
			{
				ShadowVideoSurfaceRectUsingLowPercentTable( guiSAVEBUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
			}

			SrcRect.iLeft = 536 * iPercentage / 100;
			SrcRect.iRight = SCREEN_WIDTH - iPercentage / 20;
			SrcRect.iTop = 367 * iPercentage / 100;
			SrcRect.iBottom = SCREEN_HEIGHT - 39 * iPercentage / 100;

			BltStretchVideoSurface( FRAME_BUFFER, guiSAVEBUFFER, 0, 0, 0, &SrcRect, &DstRect );
			InvalidateScreen();
			RefreshScreen( NULL );
		}
	}

	ColorFillVideoSurfaceArea( FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );
	InvalidateScreen( );
	RefreshScreen( NULL );

#ifdef JA2UB	
	//if we are going to the intro screen, return before putting up a loadscreen
	if( gbExitingMapScreenToWhere == MAP_EXIT_TO_INTRO_SCREEN )
	{
		SetPendingNewScreen( INTRO_SCREEN );
		return;
	}
#endif

	//If we are loading a saved game, use the Loading screen we saved into the SavedGameHeader file
	// ( which gets reloaded into gubLastLoadingScreenID )
	if( !gfGotoSectorTransition )
	{
		if( gTacticalStatus.uiFlags & LOADING_SAVED_GAME )
		{
			DisplayLoadScreenWithID( gubLastLoadingScreenID );
		}
		else
		{
			ubLoadScreenID = GetLoadScreenID( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
			DisplayLoadScreenWithID( ubLoadScreenID );
		}
	}

#ifdef JA2TESTVERSION
	uiEnterSectorStartTime = 0;
	uiEnterSectorEndTime = 0;
	//The grand total time for loading a map
	uiLoadWorldStartTime = 0;
	uiLoadWorldEndTime = 0;
	//The time spent in FileRead
	uiTotalFileReadTime = 0;
	uiTotalFileReadCalls = 0;
	//Sections of LoadWorld
	uiLoadWorldTime = 0;
	uiTrashWorldTime = 0;
	uiLoadMapTilesetTime = 0;
	uiLoadMapLightsTime = 0;
	uiBuildShadeTableTime = 0;
	uiEnterSectorStartTime = GetJA2Clock();
#endif

}

void EndLoadScreen( )
{
#ifdef JA2TESTVERSION
	//Report the time it took to load the map.  This is temporary until we are satisfied with the time
	//it takes to load the map.
	CHAR16 str[60];
	UINT32 uiSeconds;
	UINT32 uiHundreths;
	UINT32 uiUnaccounted;
	UINT32 uiPercentage;
	uiEnterSectorEndTime = GetJA2Clock();
	uiSeconds = (uiEnterSectorEndTime - uiEnterSectorStartTime) / 1000;
	uiHundreths = ((uiEnterSectorEndTime - uiEnterSectorStartTime) / 10) % 100;
	SetFont( FONT10ARIAL );
	SetFontForeground( FONT_YELLOW );
	SetFontBackground( FONT_NEARBLACK );
	if( !gbWorldSectorZ )
	{
		swprintf( str, L"%c%d ENTER SECTOR TIME:  %d.%02d seconds.",
							'A' + gWorldSectorY - 1, gWorldSectorX, uiSeconds, uiHundreths );
	}
	else
	{
		swprintf( str, L"%c%d_b%d ENTER SECTOR TIME:  %d.%02d seconds.",
							'A' + gWorldSectorY - 1, gWorldSectorX, gbWorldSectorZ, uiSeconds, uiHundreths );
	}
	ScreenMsg( FONT_YELLOW, MSG_TESTVERSION, str );
#ifndef USE_VFS
	FILE *fp;

	if( fStartNewFile )
	{ //start new file
		fp = fopen( "TimeResults.txt", "w" );
		ScreenMsg( FONT_YELLOW, MSG_TESTVERSION, L"See JA2\\Data\\TimeResults.txt for more detailed timings." );
		fStartNewFile = FALSE;
	}
	else
	{ //append to end of file
		fp = fopen( "TimeResults.txt", "a" );

		if ( fp )
		{
			fprintf( fp, "\n\n--------------------------------------------------------------------\n\n" );
		}
	}
	if( fp )
	{
		//Record all of the timings.
		fprintf( fp, "%S\n", str );
		fprintf( fp, "EnterSector() supersets LoadWorld().  This includes other external sections.\n");
		//FileRead()
		fprintf( fp, "\n\nVARIOUS FUNCTION TIMINGS (exclusive of actual function timings in second heading)\n" );
		uiSeconds = uiTotalFileReadTime / 1000;
		uiHundreths = (uiTotalFileReadTime / 10) % 100;
		fprintf( fp, "FileRead:  %d.%02d (called %d times)\n", uiSeconds, uiHundreths, uiTotalFileReadCalls );

		fprintf( fp, "\n\nSECTIONS OF LOADWORLD (all parts should add up to 100%)\n" );
		//TrashWorld()
		uiSeconds = uiTrashWorldTime / 1000;
		uiHundreths = (uiTrashWorldTime / 10) % 100;
		fprintf( fp, "TrashWorld: %d.%02d\n", uiSeconds, uiHundreths );
		//LoadMapTilesets()
		uiSeconds = uiLoadMapTilesetTime / 1000;
		uiHundreths = (uiLoadMapTilesetTime / 10) % 100;
		fprintf( fp, "LoadMapTileset: %d.%02d\n", uiSeconds, uiHundreths );
		//LoadMapLights()
		uiSeconds = uiLoadMapLightsTime / 1000;
		uiHundreths = (uiLoadMapLightsTime / 10) % 100;
		fprintf( fp, "LoadMapLights: %d.%02d\n", uiSeconds, uiHundreths );
		uiSeconds = uiBuildShadeTableTime / 1000;
		uiHundreths = (uiBuildShadeTableTime / 10) % 100;
		fprintf( fp, "  1)  BuildShadeTables: %d.%02d\n", uiSeconds, uiHundreths );

		uiPercentage = uiNumImagesReloaded * 100 / NUMBEROFTILETYPES;
		fprintf( fp, "  2)  %d%% of the tileset images were actually reloaded.\n", uiPercentage );
		if ( ( uiNumTablesSaved+uiNumTablesLoaded ) != 0 )
		{
			uiPercentage = uiNumTablesSaved * 100 / (uiNumTablesSaved+uiNumTablesLoaded);
		}
		else
		{
			uiPercentage = 0;
		}
		fprintf( fp, "  3)  Of that, %d%% of the shade tables were generated (not loaded).\n", uiPercentage );
		if( gfForceBuildShadeTables )
			fprintf( fp, "  NOTE:  Force building of shadetables enabled on this local computer.\n" );


		//Unaccounted
		uiUnaccounted = uiLoadWorldTime - uiTrashWorldTime - uiLoadMapTilesetTime - uiLoadMapLightsTime;
		uiSeconds = uiUnaccounted / 1000;
		uiHundreths = (uiUnaccounted / 10) % 100;
		fprintf( fp, "Unaccounted: %d.%02d\n", uiSeconds, uiHundreths );
		//LoadWorld()
		uiSeconds = uiLoadWorldTime / 1000;
		uiHundreths = (uiLoadWorldTime / 10) % 100;
		fprintf( fp, "\nTotal: %d.%02d\n", uiSeconds, uiHundreths );

		fclose( fp );
	}
#else
	sgp::Logger_ID time_log_id = sgp::Logger::instance().createLogger();
	sgp::Logger::instance().connectFile(time_log_id, L"TimeResults.txt", true, sgp::Logger::FLUSH_ON_ENDL);
	sgp::Logger::LogInstance timeResults = SGP_LOG(time_log_id);

	ScreenMsg( FONT_YELLOW, MSG_TESTVERSION, L"See JA2\\Data\\TimeResults.txt for more detailed timings." );

	//Record all of the timings.
	timeResults << str << sgp::endl;
	timeResults << "EnterSector() supersets LoadWorld().  This includes other external sections." << vfs::Log::endl;
		//FileRead()
	timeResults << sgp::endl << vfs::Log::endl << "VARIOUS FUNCTION TIMINGS (exclusive of actual function timings in second heading)" << sgp::endl;
	uiSeconds = uiTotalFileReadTime / 1000;
	uiHundreths = (uiTotalFileReadTime / 10) % 100;
	//fprintf( fp, "FileRead:  %d.%02d (called %d times)\n", uiSeconds, uiHundreths, uiTotalFileReadCalls );
	timeResults << "FileRead:  " <<uiSeconds<< "." << uiHundreths << " (called " << uiTotalFileReadCalls << " times)" << sgp::endl;

	timeResults << sgp::endl << sgp::endl << "SECTIONS OF LOADWORLD (all parts should add up to 100%)" << sgp::endl;

	//TrashWorld()
	uiSeconds = uiTrashWorldTime / 1000;
	uiHundreths = (uiTrashWorldTime / 10) % 100;
	timeResults << "TrashWorld: " << uiSeconds << "." << uiHundreths << sgp::endl;
	//LoadMapTilesets()
	uiSeconds = uiLoadMapTilesetTime / 1000;
	uiHundreths = (uiLoadMapTilesetTime / 10) % 100;
	timeResults << "LoadMapTileset: " << uiSeconds << "." << uiHundreths << sgp::endl;
	//LoadMapLights()
	uiSeconds = uiLoadMapLightsTime / 1000;
	uiHundreths = (uiLoadMapLightsTime / 10) % 100;
	timeResults << "LoadMapLights: " << uiSeconds << "." << uiHundreths << sgp::endl;

	uiSeconds = uiBuildShadeTableTime / 1000;
	uiHundreths = (uiBuildShadeTableTime / 10) % 100;
	timeResults << "  1)  BuildShadeTables: " << uiSeconds << "." << uiHundreths << sgp::endl;

	uiPercentage = uiNumImagesReloaded * 100 / NUMBEROFTILETYPES;
	timeResults << "  2)  " << uiPercentage << "% of the tileset images were actually reloaded." << sgp::endl;
	if ( ( uiNumTablesSaved+uiNumTablesLoaded ) != 0 )
	{
		uiPercentage = uiNumTablesSaved * 100 / (uiNumTablesSaved+uiNumTablesLoaded);
	}
	else
	{
		uiPercentage = 0;
	}
	timeResults << "  3)  Of that, " << uiPercentage << "% of the shade tables were generated (not loaded)." << sgp::endl;
	if( gfForceBuildShadeTables )
	{
		timeResults << "  NOTE:  Force building of shadetables enabled on this local computer." << sgp::endl;
	}

	//Unaccounted
	uiUnaccounted = uiLoadWorldTime - uiTrashWorldTime - uiLoadMapTilesetTime - uiLoadMapLightsTime;
	uiSeconds = uiUnaccounted / 1000;
	uiHundreths = (uiUnaccounted / 10) % 100;
	timeResults << "Unaccounted: " << uiSeconds << "." << uiHundreths << sgp::endl;
	//LoadWorld()
	uiSeconds = uiLoadWorldTime / 1000;
	uiHundreths = (uiLoadWorldTime / 10) % 100;
	timeResults << vfs::Log::endl << "Total: " << uiSeconds << "." << uiHundreths << sgp::endl;
#endif // USE_VFS
#endif
}


BOOLEAN InitStrategicEngine( )
{
	// this runs every time we start the application, so don't put anything in here that's only supposed to run when a new
	// *game* is started!  Those belong in InitStrategicLayer() instead.

	// set up town stuff
	BuildListOfTownSectors( );

	// town distances are pre-calculated and read in from a data file
	// since it takes quite a while to plot strategic paths between all pairs of town sectors...


//#define RECALC_TOWN_DISTANCES
//#ifdef RECALC_TOWN_DISTANCES
	CalcDistancesBetweenTowns ( );
//	WriteOutDistancesBetweenTowns( );
//	DumpDistancesBetweenTowns( );
//#endif

//	ReadInDistancesBetweenTowns( );

	return( TRUE );
}


UINT8 GetTownIdForSector( INT16 sMapX, INT16 sMapY )
{
	// return the name value of the town in this sector

	return( StrategicMap[CALCULATE_STRATEGIC_INDEX( sMapX, sMapY )].bNameId );
}


// return number of sectors this town takes up
UINT8 GetTownSectorSize( INT8 bTownId )
{
	UINT8 ubSectorSize = 0;
	INT32 iCounterA =0, iCounterB = 0;

	for( iCounterA = 0; iCounterA < ( INT32 )( MAP_WORLD_X - 1 ); iCounterA++ )
	{
		for( iCounterB = 0; iCounterB < ( INT32 )( MAP_WORLD_Y - 1 ); iCounterB++ )
		{
			if(  StrategicMap[CALCULATE_STRATEGIC_INDEX( iCounterA, iCounterB )].bNameId == bTownId )
			{
				ubSectorSize++;
			}
		}
	}

	return( ubSectorSize );
}

UINT8 GetMilitiaCountAtLevelAnywhereInTown( UINT8 ubTownValue, UINT8 ubLevelValue )
{
	INT32 iCounter = 0;
	UINT8 ubCount =0;

	while( pTownNamesList[ iCounter ] != 0 )
	{
		if( StrategicMap[ pTownLocationsList[ iCounter ] ].bNameId == ubTownValue )
		{
			// match.  Add the number of civs at this level
			ubCount += SectorInfo[ STRATEGIC_INDEX_TO_SECTOR_INFO( pTownLocationsList[ iCounter ] ) ].ubNumberOfCivsAtLevel[ ubLevelValue ];
		}

		iCounter++;
	}
	return( ubCount );
}


// return number of sectors under player control for this town
UINT8 GetTownSectorsUnderControl( INT8 bTownId )
{
	INT8 ubSectorsControlled = 0;
	INT32 iCounterA =0, iCounterB = 0;
	UINT16 usSector = 0;

	for( iCounterA = 0; iCounterA < ( INT32 )( MAP_WORLD_X - 1 ); iCounterA++ )
	{
		for( iCounterB = 0; iCounterB < ( INT32 )( MAP_WORLD_Y - 1 ); iCounterB++ )
		{
			usSector = (UINT16)CALCULATE_STRATEGIC_INDEX( iCounterA, iCounterB );

			if( ( StrategicMap[ usSector ].bNameId == bTownId ) &&
					( StrategicMap[ usSector ].fEnemyControlled == FALSE ) &&
					( NumEnemiesInSector( ( INT16 )iCounterA, ( INT16 )iCounterB ) == 0 ) )
			{
				ubSectorsControlled++;
			}
		}
	}

	return( ubSectorsControlled );
}

void InitializeSAMSites( void )
{
	UINT32 cnt;
	// move the landing zone over to Omerta
	// HEADROCK HAM 3.5: Externalized coordinates
	gsMercArriveSectorX = gGameExternalOptions.ubDefaultArrivalSectorX;
	gsMercArriveSectorY = gGameExternalOptions.ubDefaultArrivalSectorY;

	// all SAM sites start game in perfect working condition
	for ( cnt = 0; cnt < NUMBER_OF_SAMS; cnt++ )
	{
		StrategicMap[ gpSamSectorX[cnt] + ( MAP_WORLD_X * gpSamSectorY[cnt] ) ].bSAMCondition = 100;
	};
	//StrategicMap[(SAM_1_X)+(MAP_WORLD_X*(SAM_1_Y))].bSAMCondition = 100;
	//StrategicMap[(SAM_2_X)+(MAP_WORLD_X*(SAM_2_Y))].bSAMCondition = 100;
	//StrategicMap[(SAM_3_X)+(MAP_WORLD_X*(SAM_3_Y))].bSAMCondition = 100;
	//StrategicMap[(SAM_4_X)+(MAP_WORLD_X*(SAM_4_Y))].bSAMCondition = 100;

	UpdateAirspaceControl( );
}


// get short sector name without town name
void GetShortSectorString( INT16 sMapX,INT16 sMapY, STR16 sString )
{
	// OK, build string id like J11
	swprintf( sString, L"%S%S",pVertStrings[ sMapY ], pHortStrings[ sMapX ] );

	return;
}


void GetMapFileName(INT16 sMapX,INT16 sMapY, INT8 bSectorZ, STR8 bString, BOOLEAN fUsePlaceholder, BOOLEAN fAddAlternateMapLetter )
{
	CHAR8	 bTestString[ 150 ];
	CHAR8	 bExtensionString[ 15 ];

	if ( bSectorZ != 0 )
	{
		sprintf( bExtensionString, "_b%d", bSectorZ );
	}
	else
	{
		strcpy( bExtensionString, "" );
	}

	// the gfUseAlternateMap flag is set in the loading saved games.  When starting a new game the underground sector
	//info has not been initialized, so we need the flag to load an alternate sector.
	if( gfUseAlternateMap | GetSectorFlagStatus( sMapX, sMapY, bSectorZ, SF_USE_ALTERNATE_MAP ) )
	{
		gfUseAlternateMap = FALSE;

		//if we ARE to use the a map, or if we are saving AND the save game version is before 80, add the a
		if( fAddAlternateMapLetter )
		{
			strcat( bExtensionString, "_a" );
		}
	}

#ifdef JA2UB
/* Ja25: No meanwhiles */
#else
	// If we are in a meanwhile...
	if ( AreInMeanwhile( ) && sMapX == 3 && sMapY == 16 && !bSectorZ )//GetMeanwhileID() != INTERROGATION )
	{
		if( fAddAlternateMapLetter )
		{
			strcat( bExtensionString, "_m" );
		}
	}
#endif

	// This is the string to return, but...
	sprintf( bString, "%s%s%s.DAT", pVertStrings[sMapY], pHortStrings[sMapX], bExtensionString );

	// We will test against this string
	sprintf( bTestString, "MAPS\\%s", bString );

	if( fUsePlaceholder && !FileExists( bTestString ) )
	{
		// Debug str
		DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Map does not exist for %s, using default.", bTestString ) );
		// Set to a string we know!
		sprintf(bString, "H10.DAT",pVertStrings[sMapY],pHortStrings[sMapX]);
		ScreenMsg( FONT_YELLOW, MSG_DEBUG, L"Using PLACEHOLDER map!");
	}
	return;
}

void GetCurrentWorldSector( INT16 *psMapX, INT16 *psMapY )
{
	*psMapX = gWorldSectorX;
	*psMapY = gWorldSectorY;
}

//not in overhead.h!
extern UINT8 NumEnemyInSector();

void HandleRPCDescriptionOfSector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ )
{
	UINT32	cnt;
	UINT8		ubSectorDescription[33][3] =
	{
		 // row (letter), column, quote #
		 { 2, 13,  0 },	// b13	Drassen
		 { 3, 13,  1 },	// c13	Drassen
		 { 4, 13,  2 },	// d13	Drassen
		 { 8, 13,  3 },	// h13	Alma
		 { 8, 14,  4 },	// h14	Alma
		 { 9, 13,  5 },	// i13	Alma * (extra quote 6 if Sci-fi ) *
		 { 9, 14,  7 },	// i14	Alma
		 { 6,  8,  8 },	// f8	Cambria
		 { 6,  9,  9 }, 	// f9	Cambria
		 { 7,  8, 10 },	// g8	Cambria

		 { 7,  9, 11 },	// g9	Cambria
		 { 3,  6, 12 },	// c6	San Mona
		 { 3,  5, 13 },	// c5	San Mona
		 { 4,  5, 14 },	// d5	San Mona
		 { 2,  2, 15 },	// b2	Chitzena
		 { 1,  2, 16 },	// a2	Chitzena
		 { 7,  1, 17 },	// g1	Grumm
		 { 8,  1, 18 },	// h1	Grumm
		 { 7,  2, 19 },	// g2 	Grumm
		 { 8,  2, 20 },	// h2	Grumm

		 { 9,  6, 21 },	// i6	Estoni
		 {11,	 4, 22 },	// k4	Orta
		 {12,	 11, 23 },	// l11	Balime
		 {12,	 12, 24 },	// l12	Balime
		 {15,  3, 25 },	// o3	Meduna
		 {16,  3, 26 },	// p3	Meduna
		 {14,  4, 27 },	// n4	Meduna
		 {14,  3, 28 },	// n3	Meduna
		 {15,  4, 30 },	// o4	Meduna
		 {10,  9, 31 },	// j9	Tixa

		 {4,	15, 32 },	// d15	NE SAM
		 {4,  2, 33 },	// d2	NW SAM
		 {9,  8, 34 }	// i8	CENTRAL SAM
	};

  // Default to false
	gTacticalStatus.fCountingDownForGuideDescription = FALSE;


	// OK, if the first time in...
	if ( GetSectorFlagStatus( sSectorX, sSectorY, ( UINT8 )sSectorZ, SF_HAVE_USED_GUIDE_QUOTE ) != TRUE )
	{
		if ( sSectorZ != 0 )
		{
			return;
		}

		// OK, check if we are in a good sector....
		for ( cnt = 0; cnt < 33; cnt++ )
		{
			if ( sSectorX == ubSectorDescription[ cnt ][ 1 ] && sSectorY == ubSectorDescription[ cnt ][ 0 ] )
			{
				if ((gGameOptions.ubGameStyle == STYLE_REALISTIC || !gGameExternalOptions.fEnableCrepitus) && cnt == 3)
					continue;

				/*
				// If we're not scifi, skip some
				if ( !(gGameOptions.ubGameStyle == STYLE_SCIFI && gGameExternalOptions.fEnableCrepitus) && cnt == 3 )
				{
					continue;
				}
				*/

				SetSectorFlag( sSectorX, sSectorY, ( UINT8 )sSectorZ, SF_HAVE_USED_GUIDE_QUOTE );

				gTacticalStatus.fCountingDownForGuideDescription = TRUE;
				gTacticalStatus.bGuideDescriptionCountDown			 = (INT8)( 4 + Random( 5 ) ); // 4 to 8 tactical turns...
				gTacticalStatus.ubGuideDescriptionToUse					 = ubSectorDescription[ cnt ][ 2 ];
				gTacticalStatus.bGuideDescriptionSectorX				 =  (INT8)sSectorX;
				gTacticalStatus.bGuideDescriptionSectorY				 =  (INT8)sSectorY;
			}
		}
	}

  // Handle guide description ( will be needed if a SAM one )
#ifdef JA2UB
//UB
#else
	HandleRPCDescription( );
#endif
}


BOOLEAN	SetCurrentWorldSector( INT16 sMapX, INT16 sMapY, INT8 bMapZ )
{
	BOOLEAN									fChangeMusic = TRUE;

#ifdef CRIPPLED_VERSION
	if( sMapY >= 5 && sMapY != 16 )
	{
		CrippledVersionFailureToLoadMapCheck();
		return FALSE;
	}
#endif

	// ATE: Zero out accounting functions
	memset( gbMercIsNewInThisSector, 0, sizeof( gbMercIsNewInThisSector ) );

	SyncStrategicTurnTimes();

#ifdef JA2BETAVERSION
	if( gfOverrideSector )
	{
		//skip the cancel, and force load the sector.  This is used by the AIViewer to "reset" a level with
		//different numbers of various types of enemies.
	}
	else
#endif
		// reset goggle warning
		gogglewarning = FALSE;
		// is the sector already loaded?
		if( ( gWorldSectorX == sMapX ) && ( sMapY == gWorldSectorY) && ( bMapZ == gbWorldSectorZ) )
		{
			//Inserts the enemies into the already loaded map based on the strategic information.
			//Note, the flag will return TRUE only if enemies were added.  The game may wish to
			//do something else in a case where no enemies are present.

			PrepareMilitiaForTactical( FALSE);

			SetPendingNewScreen(GAME_SCREEN);
			if( !NumEnemyInSector( ) )
			{
				if (is_networked)
				{
					if(is_server && gEnemyEnabled == 1)
						PrepareEnemyForSectorBattle();
				}
				else
				{
					PrepareEnemyForSectorBattle();
				}
			}

			for (int i=0; i<TOTAL_SOLDIERS; i++)
			{
				//CHRISL: We should only bother with this assertion if the soldier is alive.  Dead soliders are moved to
				//	GridNo = NOWHERE, which causes this assertion to fail
				//CHRISL: There's also an issue with vehicles.  Soldiers in any vehicle are considered to be in sGridNo = NOWHERE
				//	This will result in an assertion error, so let's skip the assertion if the merc is assigned to a vehicle
				if(!(MercPtrs[i]->flags.uiStatusFlags & SOLDIER_DEAD) && MercPtrs[i]->bAssignment != VEHICLE)
				{
					//Assert( !MercPtrs[i]->bActive || !MercPtrs[i]->bInSector || MercPtrs[i]->sGridNo != NOWHERE || MercPtrs[i]->bVehicleID == iHelicopterVehicleId );
					Assert( !MercPtrs[i]->bActive || !MercPtrs[i]->bInSector || !TileIsOutOfBounds(MercPtrs[i]->sGridNo) || MercPtrs[i]->bVehicleID == iHelicopterVehicleId );
				}
			}

			if( gubNumCreaturesAttackingTown && !gbWorldSectorZ &&
				gubSectorIDOfCreatureAttack == SECTOR( gWorldSectorX, gWorldSectorY ) )
			{
				if (is_networked)
				{
					if(is_server && gCreatureEnabled == 1)
						PrepareCreaturesForBattle();
				}
				else
				{
					PrepareCreaturesForBattle();
				}
			}
			if( gfGotoSectorTransition )
			{
				BeginLoadScreen( );
				gfGotoSectorTransition = FALSE;
			}

			// Check for helicopter being on the ground in this sector...
			HandleHelicopterOnGroundGraphic( );

			// 0verhaul:  Okay, it is apparent that the enemies are not reset correctly.  So I will now try to add symmetry
			// between enemy placement and militia placement.  The enemies do have one advantage here, though:  If a sector
			// is in enemy hands, then their sector is not actually loaded.  At least not normally.  Perhaps it would be useful
			// to lose a battle with one group of mercs, then bring in another group without changing to another sector to see
			// if enemy integrity holds.  But this would require the enemies to come up with reinforcements inbetween the battles
			// to really test out.
			//
			// Anyway, for now I will remove this call from here.  The objective is to add militia any time we could add enemies
			// except for the case of training new militia.  But that case can be handled each time militia training finishes.
//			ResetMilitia();
			AllTeamsLookForAll( TRUE );

			for (int i=0; i<TOTAL_SOLDIERS; i++)
			{
				//CHRISL: We should only bother with this assertion if the soldier is alive.  Dead soliders are moved to
				//	GridNo = NOWHERE, which causes this assertion to fail
				//CHRISL: There's also an issue with vehicles.  Soldiers in any vehicle are considered to be in sGridNo = NOWHERE
				//	This will result in an assertion error, so let's skip the assertion if the merc is assigned to a vehicle
				if(!(MercPtrs[i]->flags.uiStatusFlags & SOLDIER_DEAD) && MercPtrs[i]->bAssignment != VEHICLE)
				{
					//Assert( !MercPtrs[i]->bActive || !MercPtrs[i]->bInSector || MercPtrs[i]->sGridNo != NOWHERE || MercPtrs[i]->bVehicleID == iHelicopterVehicleId );
					Assert( !MercPtrs[i]->bActive || !MercPtrs[i]->bInSector || !TileIsOutOfBounds(MercPtrs[i]->sGridNo) || MercPtrs[i]->bVehicleID == iHelicopterVehicleId );
				}
			}

			return( TRUE );
		}

		if( gWorldSectorX && gWorldSectorY && gbWorldSectorZ != -1 )
		{
			HandleDefiniteUnloadingOfWorld( ABOUT_TO_LOAD_NEW_MAP );
		}

		// make this the currently loaded sector
		gWorldSectorX  = sMapX;
		gWorldSectorY  = sMapY;
		gbWorldSectorZ = bMapZ;

		// update currently selected map sector to match
		ChangeSelectedMapSector( sMapX, sMapY, bMapZ );


		//Check to see if the sector we are loading is the cave sector under Tixa.  If so
		//then we will set up the meanwhile scene to start the creature quest.
		if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
		{
			StopAnyCurrentlyTalkingSpeech( );

#ifdef JA2UB
/*  Ja25 no creatures  */
#else			
			if( gWorldSectorX == 9 && gWorldSectorY == 10 && gbWorldSectorZ == 2 )
			{
				InitCreatureQuest(); //Ignored if already active.
			}
#endif
		}

		//Stop playing any music -- will fade out.
		// SetMusicMode( MUSIC_NONE );

		// ATE: Determine if we should set the default music...

		// Are we already in 'tense' music...

		// ATE: Change music only if not loading....
		/*-
		if ( gubMusicMode == MUSIC_TACTICAL_ENEMYPRESENT  )
		{
		fChangeMusic = FALSE;
		}

		// Did we 'tactically traverse' over....
		if ( gfTacticalTraversal )
		{
		fChangeMusic = FALSE;
		}

		// If we have no music playing at all....
		if ( gubMusicMode == MUSIC_NONE  )
		{
		fChangeMusic = TRUE;
		}
		-*/

		if( (gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
		{
			fChangeMusic = TRUE;
		}
		else
		{
			fChangeMusic = FALSE;
		}


		if ( fChangeMusic )
		{
			SetMusicMode( MUSIC_MAIN_MENU );
		}

		// ATE: Do this stuff earlier!
		if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
		{
			// Update the last time we were in tactical...
			gTacticalStatus.uiTimeSinceLastInTactical = GetWorldTotalMin( );

			// init some AI stuff
			InitializeTacticalStatusAtBattleStart();

			// CJC: delay this until after entering the sector!
			//InitAI();

			// Check for helicopter being on the ground in this sector...
			HandleHelicopterOnGroundSkyriderProfile( );
#ifdef JA2UB			
		//Check to see if we should add Manuel to this sector, if so add him
		ShouldNpcBeAddedToSector( gWorldSectorX, gWorldSectorY, bMapZ );
#endif		
		}

		//Load and enter the new sector
		if( EnterSector( gWorldSectorX, gWorldSectorY, bMapZ ) )
		{
			// CJC: moved this here Feb 17
			if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
			{
				InitAI();
			}

			//If there are any people with schedules, now is the time to process them.
			//CJC: doesn't work here if we're going through the tactical placement GUI; moving
			// this call to PrepareLoadedSector()
			//PostSchedules();

			// ATE: OK, add code here to update the states of doors if they should
			// be closed......
			if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
			{
				ExamineDoorsOnEnteringSector( );
			}

			// Update all the doors in the sector according to the temp file previously
			// loaded, and any changes made by the schedules
			UpdateDoorGraphicsFromStatus( TRUE, FALSE );

			//Set the fact we have visited the  sector
			SetSectorFlag( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, SF_ALREADY_LOADED );

			// Check for helicopter being on the ground in this sector...
			HandleHelicopterOnGroundGraphic( );
		}
		else
			return( FALSE );

		if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
		{
			if( (GetMusicMode() != MUSIC_TACTICAL_ENEMYPRESENT && GetMusicMode() != MUSIC_TACTICAL_BATTLE) ||
				(!NumHostilesInSector( sMapX, sMapY, bMapZ ) && GetMusicMode() == MUSIC_TACTICAL_ENEMYPRESENT) )
			{
				// ATE; Fade FA.T....
				SetMusicFadeSpeed( 5 );

				SetMusicMode( MUSIC_TACTICAL_NOTHING );
			}
			
	#ifdef JA2UB			
			// Say a quote from a merc who has new lines for the sector ( if any )
			HandlePlayerTeamQuotesWhenEnteringSector( sMapX, sMapY, bMapZ ); 
	#else
			// ATE: Check what sector we are in, to show description if we have an RPC.....
			HandleRPCDescriptionOfSector( sMapX, sMapY, bMapZ );
	#endif


			// ATE: Set Flag for being visited...
			SetSectorFlag( sMapX, sMapY, bMapZ, SF_HAS_ENTERED_TACTICAL );
#ifdef JA2UB			
			//ja2ub
			// If any emails should be sent from this sector
			HandleEmailBeingSentWhenEnteringSector( sMapX, sMapY, bMapZ, FALSE );
#endif

			// ATE; Reset some flags for creature sayings....
			gTacticalStatus.fSaidCreatureFlavourQuote = FALSE;
			gTacticalStatus.fHaveSeenCreature					= FALSE;
			gTacticalStatus.fBeenInCombatOnce					= FALSE;
			gTacticalStatus.fSaidCreatureSmellQuote		= FALSE;
			ResetMultiSelection( );

			// ATE: Decide if we can have crows here....
			gTacticalStatus.fGoodToAllowCrows					= FALSE;
			gTacticalStatus.fHasEnteredCombatModeSinceEntering = FALSE;
			gTacticalStatus.fDontAddNewCrows          = FALSE;
#ifdef JA2UB			
		//Call this function, if Jerry doesnt need to be added, it will return
		UpdateJerryMiloInInitialSector();
#endif
			// Adjust delay for tense quote
			gTacticalStatus.sCreatureTenseQuoteDelay = (INT16)( 10 + Random( 20 ) );

			{
				INT16 sWarpWorldX;
				INT16 sWarpWorldY;
				INT8  bWarpWorldZ;
				INT32 sWarpGridNo;

				if ( GetWarpOutOfMineCodes( &sWarpWorldX, &sWarpWorldY, &bWarpWorldZ, &sWarpGridNo ) && gbWorldSectorZ >= 2 )
				{
					gTacticalStatus.uiFlags |= IN_CREATURE_LAIR;
				}
				else
				{
					gTacticalStatus.uiFlags &= ( ~IN_CREATURE_LAIR );
				}
			}

			// Every third turn
			//if ( Random( 3 ) == 0  )
			{
				if(gGameSettings.fOptions[ TOPTION_ALLOW_CROWS ] == TRUE)
				{
					gTacticalStatus.fGoodToAllowCrows	= TRUE;
					gTacticalStatus.ubNumCrowsPossible	= (UINT8)( 5 + Random( 5 ) );
				}
			}

		}

		return( TRUE );
}

BOOLEAN MapExists( UINT8 * szFilename )
{
	CHAR8 str[50];
	HWFILE fp;
	sprintf( str, "MAPS\\%s", szFilename );
	fp = FileOpen( str, FILE_ACCESS_READ, FALSE );
	if( !fp )
		return FALSE;
	FileClose( fp );
	return TRUE;
}

void RemoveMercsInSector( )
{
	INT32					cnt;
	SOLDIERTYPE		*pSoldier;

	// IF IT'S THE SELECTED GUY, MAKE ANOTHER SELECTED!
	cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;

	// ATE: only for OUR guys.. the rest is taken care of in TrashWorld() when a new sector is added...
  for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++, pSoldier++)
	{
		if ( pSoldier->bActive  )
		{
			pSoldier->RemoveSoldierFromGridNo( );
		}
	}

}

void PrepareLoadedSector()
{
	BOOLEAN fEnemyPresenceInThisSector = FALSE;
	BOOLEAN fAddCivs = TRUE;
	INT8 bMineIndex = -1;
#ifdef JA2UB
//Ja25 No meanwhiles
#else
	if( AreInMeanwhile( ) == FALSE )
#endif
	{
		if( gbWorldSectorZ == 0 )
		{
//			MakePlayerPerceptionOfSectorControlCorrect( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
		}
		else
		{
			// we always think we control underground sectors once we've visited them
			SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ].fPlayer[ gbWorldSectorZ ] = TRUE;
		}
	}

	if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
	{
		UpdateMercsInSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
	}

	// Reset ambients!
	HandleNewSectorAmbience( gTilesets[ giCurrentTilesetID ].ubAmbientID );

	//if we are loading a 'pristine' map ( ie, not loading a saved game )
	if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ))
	{

#ifdef JA2UB
//Ja25 No meanwhiles
#else
		if ( !AreReloadingFromMeanwhile( ) )
#endif
		{
			SetPendingNewScreen(GAME_SCREEN);

      // Make interface the team panel always...
			SetCurrentInterfacePanel( (UINT8)TEAM_PANEL );
		}


		//Check to see if civilians should be added.  Always add civs to maps unless they are
		//in a mine that is shutdown.
		if( gbWorldSectorZ )
		{
			bMineIndex = GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
			if( bMineIndex != -1 )
			{
				if( !AreThereMinersInsideThisMine( (UINT8)bMineIndex ) )
				{
					fAddCivs = FALSE;
				}
			}
		}

		if (is_networked)
		{
			if( is_server && fAddCivs && gCivEnabled == 1)//hayden its around here we apply .ini choices for Ai
			{
				AddSoldierInitListTeamToWorld( CIV_TEAM, 255 );
			}
		}
		else if (fAddCivs)
		{
			AddSoldierInitListTeamToWorld( CIV_TEAM, 255 );
		}

		if (is_networked)
		{
			if(is_server && gMilitiaEnabled == 1)
				AddSoldierInitListTeamToWorld( MILITIA_TEAM, 255 );
		}
		else
		{
			AddSoldierInitListTeamToWorld( MILITIA_TEAM, 255 );
		}
		
		if (is_networked)
		{
			if(is_server && gCreatureEnabled == 1)
				AddSoldierInitListBloodcats();
		}
		else
		{
			AddSoldierInitListBloodcats();
		}


		//Creatures are only added if there are actually some of them.  It has to go through some
		//additional checking.

		#ifdef JA2TESTVERSION
		//Override the sector with the populations specified in the AIViewer
		if( gfOverrideSector )
		{
			if( gbWorldSectorZ > 0 )
			{
				UNDERGROUND_SECTORINFO *pSector;
				pSector = FindUnderGroundSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
				pSector->ubNumAdmins = (UINT8)( gsAINumAdmins > 0 ? gsAINumAdmins : 0 );
				pSector->ubNumTroops = (UINT8)( gsAINumTroops > 0 ? gsAINumTroops : 0 );
				pSector->ubNumElites = (UINT8)( gsAINumElites > 0 ? gsAINumElites : 0 );
				pSector->ubNumCreatures = (UINT8)( gsAINumCreatures > 0 ? gsAINumCreatures : 0 );
			}
			else if( !gbWorldSectorZ )
			{
				SECTORINFO *pSector;
				pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ];
				pSector->ubNumAdmins = (UINT8)( gsAINumAdmins > 0 ? gsAINumAdmins : 0 );
				pSector->ubNumTroops = (UINT8)( gsAINumTroops > 0 ? gsAINumTroops : 0 );
				pSector->ubNumElites = (UINT8)( gsAINumElites > 0 ? gsAINumElites : 0 );
				pSector->ubNumCreatures = (UINT8)( gsAINumCreatures > 0 ? gsAINumCreatures : 0 );
			}
		}
		#endif

		// Haydent
		if (is_networked)
		{
			if(is_server && gCreatureEnabled == 1)
				PrepareCreaturesForBattle();

			// Haydent
			if(is_server && gMilitiaEnabled == 1)
				PrepareMilitiaForTactical(FALSE);
		}
		else
		{
			PrepareCreaturesForBattle();
			PrepareMilitiaForTactical(TRUE);
		}

		// OK, set varibles for entring this new sector...
		gTacticalStatus.fVirginSector = TRUE;

		//Inserts the enemies into the newly loaded map based on the strategic information.
		//Note, the flag will return TRUE only if enemies were added.  The game may wish to
		//do something else in a case where no enemies are present.
		if( !gfRestoringEnemySoldiersFromTempFile )
		{
			//AddSoldierInitListTeamToWorld( CIV_TEAM, 255 );
//			fEnemyPresenceInThisSector = PrepareEnemyForSectorBattle();
		}
		
		if (is_networked)
		{
			if(is_server && gCivEnabled == 1)
				AddProfilesNotUsingProfileInsertionData(); //hayden: is just for civ's
		}
		else
		{
			AddProfilesNotUsingProfileInsertionData();
		}
#ifdef JA2UB
//Ja25 No meanwhiles
#else
		if( !AreInMeanwhile() || GetMeanwhileID() == INTERROGATION )
#endif
		{
			if (is_networked)
			{
				if(is_server && gEnemyEnabled == 1)
					fEnemyPresenceInThisSector = PrepareEnemyForSectorBattle();
			}
			else
			{
				fEnemyPresenceInThisSector = PrepareEnemyForSectorBattle();
			}
		}


		//Regardless whether or not this was set, clear it now.
		gfRestoringEnemySoldiersFromTempFile = FALSE;

		//KM:  FEB 8, 99 -- This call is no longer required!  Done already when group arrives in sector.
		//if( ( gbWorldSectorZ == 0 ) && ( fEnemyPresenceInThisSector == FALSE ) )
		//{
		//	SetThisSectorAsPlayerControlled( gWorldSectorX, gWorldSectorY, 0 );
		//}

		if( gbWorldSectorZ > 0 )
		{
			// we always think we control underground sectors once we've visited them
			SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ].fPlayer[ gbWorldSectorZ ] = TRUE;
		}

		//@@@Evaluate
		//Add profiles to world using strategic info, not editor placements.
		AddProfilesUsingProfileInsertionData();

		PostSchedules();
	}

	if( gubEnemyEncounterCode == ENEMY_AMBUSH_CODE || gubEnemyEncounterCode == BLOODCAT_AMBUSH_CODE )
	{
		if( gMapInformation.sCenterGridNo != -1 )
		{
			CallAvailableEnemiesTo( gMapInformation.sCenterGridNo );
		}
		else
		{
			#ifdef JA2BETAVERSION
				ScreenMsg( FONT_RED, MSG_ERROR, L"Ambush aborted in sector %c%d -- no center point in map.  LC:1",
					gWorldSectorY + 'A' - 1, gWorldSectorX );
			#endif
		}
	}

	EndLoadScreen( );

	if( !( gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
	{
		// unpause game
		UnPauseGame( );
	}

	gpBattleGroup = NULL;

	if( gfTacticalTraversal )
	{
		CalculateNonPersistantPBIInfo();
	}

	ScreenMsg( FONT_YELLOW, MSG_DEBUG, L"Current Time is: %d", GetWorldTotalMin() );

#ifdef JA2UB	
	HandleSectorSpecificModificatioToMap( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, FALSE );

	//If there needs to be modifications done to the enemy exp levels...
	HandleJa25EnemyExpLevelModifier( );
#endif

	AllTeamsLookForAll( TRUE );
}

#define RANDOM_HEAD_MINERS 4
void HandleQuestCodeOnSectorEntry( INT16 sNewSectorX, INT16 sNewSectorY, INT8 bNewSectorZ )
{
	//UINT8		ubRandomMiner[RANDOM_HEAD_MINERS] = { 106, 156, 157, 158 };
	//UINT8		ubMiner, ubMinersPlaced, ubMine;
	UINT8		ubThisMine;
	UINT8		cnt;
	SOLDIERTYPE * pSoldier;

	// are we in a mine sector, on the surface?
	if ( IsThereAMineInThisSector( sNewSectorX, sNewSectorY ) && ( bNewSectorZ == 0 ))
	{
		if ( CheckFact( FACT_MINERS_PLACED, 0 ) == FALSE )
		{
			// SET HEAD MINER LOCATIONS

			ubThisMine = GetMineIndexForSector( sNewSectorX, sNewSectorY );
			// hardcoded logic has been moved to Lua script
			if (g_luaMines.InitializeHeadMiners(ubThisMine))
				SetFactTrue( FACT_MINERS_PLACED );
#if 0
			//if (ubThisMine != MINE_SAN_MONA) // San Mona is abandoned
			if (!gMineStatus[ubThisMine].fEmpty)
			{
				ubMinersPlaced = 0;

				if (ubThisMine != MINE_ALMA)
				{
					// Fred Morris is always in the first mine sector we enter, unless that's Alma (then he's randomized, too)
					gMercProfiles[ 106 ].sSectorX = sNewSectorX;
					gMercProfiles[ 106 ].sSectorY = sNewSectorY;
					gMercProfiles[ 106 ].bSectorZ = 0;
					//gMercProfiles[ 106 ].bTown = gMineLocation[ ubThisMine ].bAssociatedTown;
					gMercProfiles[ 106 ].bTown = gMineStatus[ ubThisMine ].bAssociatedTown;

					// mark miner as placed
					ubRandomMiner[ 0 ] = 0;
					ubMinersPlaced++;
				}

				// assign the remaining (3) miners randomly
				for ( ubMine = 0; ubMine < MAX_NUMBER_OF_MINES; ubMine++ )
				{
					if ( ubMine == ubThisMine || ubMine == MINE_ALMA || ubMine == MINE_SAN_MONA )
					{
						// Alma always has Matt as a miner, and we have assigned Fred to the current mine
						// and San Mona is abandoned
						continue;
					}

					do
					{
						ubMiner = (UINT8) Random( RANDOM_HEAD_MINERS );
					}
					while( ubRandomMiner[ ubMiner ] == 0 );

					GetMineSector( ubMine, (INT16 *)&(gMercProfiles[ ubRandomMiner[ ubMiner ] ].sSectorX),
											(INT16 *)&(gMercProfiles[ ubRandomMiner[ ubMiner ] ].sSectorY) );
					gMercProfiles[ ubRandomMiner[ ubMiner ] ].bSectorZ = 0;
					//gMercProfiles[ ubRandomMiner[ ubMiner ] ].bTown = gMineLocation[ ubMine ].bAssociatedTown;
					gMercProfiles[ ubRandomMiner[ ubMiner ] ].bTown = gMineStatus[ ubMine ].bAssociatedTown;

					// mark miner as placed
					ubRandomMiner[ ubMiner ] = 0;
					ubMinersPlaced++;

					if (ubMinersPlaced == RANDOM_HEAD_MINERS)
					{
						break;
					}
				}

				SetFactTrue( FACT_MINERS_PLACED );
			}
#endif
		}
	}
	
	if ( CheckFact( FACT_ALL_TERRORISTS_KILLED, 0 ) )
	{
		// end terrorist quest
		// SANDRO - removed ending quest from here, placed to Interface Dialogue
		//EndQuest( QUEST_KILL_TERRORISTS, gMercProfiles[ CARMEN ].sSectorX, gMercProfiles[ CARMEN ].sSectorY );
		// remove Carmen
		gMercProfiles[ CARMEN ].sSectorX = 0;
		gMercProfiles[ CARMEN ].sSectorY = 0;
		gMercProfiles[ CARMEN ].bSectorZ = 0;
	}

	// Check to see if any player merc has the Chalice; if so,
	// note it as stolen
	cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;

	for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++)
	{
		if ( pSoldier->bActive )
		{
			if ( FindObj( pSoldier, CHALICE ) != ITEM_NOT_FOUND )
			{
				SetFactTrue( FACT_CHALICE_STOLEN );
			}
		}
	}
	
	if ( (gubQuest[ QUEST_KINGPIN_MONEY ] == QUESTINPROGRESS) && CheckFact( FACT_KINGPIN_CAN_SEND_ASSASSINS, 0 ) && (GetTownIdForSector( sNewSectorX, sNewSectorY ) != BLANK_SECTOR) && Random( 10 + GetNumberOfMilitiaInSector( sNewSectorX, sNewSectorY, bNewSectorZ ) ) < 3 )
	{
		DecideOnAssassin();
	}
	
	if ( CheckFact( FACT_ROBOT_RECRUITED_AND_MOVED, 0 ) == FALSE )
	{
		SOLDIERTYPE * pRobot;
		pRobot = FindSoldierByProfileID( ROBOT, TRUE );
		if (pRobot)
		{
			// robot is on our team and we have changed sectors, so we can
			// replace the robot-under-construction in Madlab's sector
			RemoveGraphicFromTempFile( gsRobotGridNo, SEVENTHISTRUCT1, gMercProfiles[MADLAB].sSectorX, gMercProfiles[MADLAB].sSectorY, gMercProfiles[MADLAB].bSectorZ );
			SetFactTrue( FACT_ROBOT_RECRUITED_AND_MOVED );
		}
	}
	
	#ifdef LUA_HANDLE_QUEST_CODE_ON_SECTOR
		
	LuaHandleQuestCodeOnSector( sNewSectorX, sNewSectorY, bNewSectorZ, 1);

	#else

/*
	if ( sNewSectorX == 5 && sNewSectorY == MAP_ROW_C )
	{
		// reset Madame Layla counters
		gMercProfiles[ MADAME ].bNPCData = 0;
		gMercProfiles[ MADAME ].bNPCData2 = 0;
	}
	*/

	if ( sNewSectorX == 6 && sNewSectorY == MAP_ROW_C && gubQuest[ QUEST_RESCUE_MARIA ] == QUESTDONE )
	{
		// make sure Maria and Angel are gone
		gMercProfiles[ MARIA ].sSectorX = 0;
		gMercProfiles[ ANGEL ].sSectorY = 0;
		gMercProfiles[ MARIA ].sSectorX = 0;
		gMercProfiles[ ANGEL ].sSectorY = 0;
	}

	if ( sNewSectorX == 5 && sNewSectorY == MAP_ROW_D )
	{
		gubBoxerID[ 0 ] = NOBODY;
		gubBoxerID[ 1 ] = NOBODY;
		gubBoxerID[ 2 ] = NOBODY;
	}

	if ( sNewSectorX == 3 && sNewSectorY == MAP_ROW_P )
	{
		// heal up Elliot if he's been hurt
		if ( gMercProfiles[ ELLIOT ].bLife < gMercProfiles[ ELLIOT ].bLifeMax )
		{
			gMercProfiles[ ELLIOT ].bLife = gMercProfiles[ ELLIOT ].bLifeMax;
		}
	}
	
	#endif
	
	ResetOncePerConvoRecordsForAllNPCsInLoadedSector();
}

void HandleQuestCodeOnSectorExit( INT16 sOldSectorX, INT16 sOldSectorY, INT8 bOldSectorZ )
{

#ifdef JA2UB
SOLDIERTYPE *pSoldier=NULL;
#endif

	#ifdef LUA_HANDLE_QUEST_CODE_ON_SECTOR
	
	LuaHandleQuestCodeOnSector( sOldSectorX, sOldSectorY, bOldSectorZ, 0);

	#else
	
	if ( sOldSectorX == KINGPIN_MONEY_SECTOR_X && sOldSectorY == KINGPIN_MONEY_SECTOR_Y && bOldSectorZ == KINGPIN_MONEY_SECTOR_Z )
	{
		CheckForKingpinsMoneyMissing( TRUE );
	}

	if ( sOldSectorX == 13 && sOldSectorY == MAP_ROW_H && bOldSectorZ == 0 && CheckFact( FACT_CONRAD_SHOULD_GO, 0 ) )
	{
		// remove Conrad from the map
		gMercProfiles[ CONRAD ].sSectorX = 0;
		gMercProfiles[ CONRAD ].sSectorY = 0;
	}
#ifdef JA2UB	
	//JA25 UB
	if( sOldSectorX == 7 && sOldSectorY == MAP_ROW_H && bOldSectorZ == 0 )
	{
		// remove Jerry from the map
		gMercProfiles[ 76 ].sSectorX = 0;
		gMercProfiles[ 76 ].sSectorY = 0;
	}

	//if the player is leaving a sector with  Tex in it
	if( sOldSectorX == gMercProfiles[ 64 ].sSectorX && sOldSectorY == gMercProfiles[ 64 ].sSectorY && bOldSectorZ == 0 && gMercProfiles[ 64 ].ubLastDateSpokenTo != 0 )
	{
		pSoldier = FindSoldierByProfileID( 64, TRUE );

		//if the npc isnt on the players team AND the player has never spoken to them
		if( pSoldier == NULL && gMercProfiles[ 64 ].ubLastDateSpokenTo != 0 )
		{
			// remove Tex from the map
			gMercProfiles[ 64 ].sSectorX = 0;
			gMercProfiles[ 64 ].sSectorY = 0;
		}
	}

	//if the player is leaving a sector with  John kulba in it
	if( sOldSectorX == gMercProfiles[ 62 ].sSectorX && sOldSectorY == gMercProfiles[ 62 ].sSectorY && bOldSectorZ == 0 && gMercProfiles[ 62 ].ubLastDateSpokenTo != 0 )
	{
		pSoldier = FindSoldierByProfileID( 62, TRUE );

		//if the npc isnt on the players team AND the player has never spoken to them
		if( pSoldier == NULL && gMercProfiles[ 62 ].ubLastDateSpokenTo != 0 )
		{
			// remove Tex from the map
			gMercProfiles[ 62 ].sSectorX = 0;
			gMercProfiles[ 62 ].sSectorY = 0;
		}
	}

	//if the player is leaving a sector with  Manuel in it
	if( sOldSectorX == gMercProfiles[ 60 ].sSectorX && sOldSectorY == gMercProfiles[ 60 ].sSectorY && bOldSectorZ == 0 )
	{
		pSoldier = FindSoldierByProfileID( 60, TRUE );

		//if the npc isnt on the players team AND the player has never spoken to them
		if( pSoldier == NULL && gMercProfiles[ 60 ].ubLastDateSpokenTo != 0 )
		{
			// remove Manuel from the map
			gMercProfiles[ 60 ].sSectorX = 0;
			gMercProfiles[ 60 ].sSectorY = 0;
		}
	}
#endif
	if ( sOldSectorX == HOSPITAL_SECTOR_X && sOldSectorY == HOSPITAL_SECTOR_Y && bOldSectorZ == HOSPITAL_SECTOR_Z )
	{
		CheckForMissingHospitalSupplies();
	}

	// reset the state of the museum alarm for Eldin's quotes
	SetFactFalse( FACT_MUSEUM_ALARM_WENT_OFF );
	
	#endif
}

BOOLEAN EnterSector( INT16 sSectorX, INT16 sSectorY , INT8 bSectorZ )
{
	INT32 i;
	UNDERGROUND_SECTORINFO *pNode = NULL;
	CHAR8 bFilename[ 50 ];

	// pause game
	PauseGame( );

	// stop time for this frame
	InterruptTime( );

	//Setup the tactical existance of RPCs and CIVs in the last sector before moving on to a new sector.
	//@@@Evaluate
	if( gfWorldLoaded )
	{
		for( i = gTacticalStatus.Team[ CIV_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; i++ )
		{
			if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->bInSector )
			{
				SetupProfileInsertionDataForSoldier( MercPtrs[ i ] );
			}
		}
	}

	if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
	{
		// Handle NPC stuff related to changing sectors
		HandleQuestCodeOnSectorEntry( sSectorX, sSectorY, bSectorZ );
	}

	// Begin Load
	BeginLoadScreen( );

	// This has tobe done before loadworld, as it will remmove old gridnos if present
	RemoveMercsInSector( );

	// SANDRO - reset number of enemies here
	if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
	{	
		memset( &(gTacticalStatus.bNumFoughtInBattle), 0, MAXTEAMS ); 
	}

#ifdef JA2UB
//Ja25 No meanwhiles
#else
	if( AreInMeanwhile() == FALSE )
#endif
	{
		SetSectorFlag( sSectorX, sSectorY, bSectorZ, SF_ALREADY_VISITED );
	}


	GetMapFileName( sSectorX, sSectorY, bSectorZ,  bFilename, TRUE, TRUE );

	//Load the placeholder map if the real map doesn't exist.
	if( !MapExists((UINT8 *) bFilename ) )
	{
		if( !MapExists( (UINT8 *)"Placeholder1.dat" ) )
			AssertMsg( 0, "Failed to find Placeholder1.dat (placeholder map)." );
	}

	CreateLoadingScreenProgressBar();
	#ifdef JA2BETAVERSION
	//set the font
	SetProgressBarMsgAttributes( 0, FONT12ARIAL, FONT_MCOLOR_WHITE, 0 );

	//Set the tile so we don see the text come up
	SetProgressBarTextDisplayFlag( 0, TRUE, TRUE, TRUE );
	#endif

	//CreateProgressBar( 0, 160, 380, 480, 400 );
	//#ifdef JA2TESTVERSION
	//	//add more detailed progress bar
	//	DefineProgressBarPanel( 0, 65, 79, 94, 130, 350, 510, 430 );
	//	swprintf( str, L"Loading map:  %S", bFilename );
	//	SetProgressBarTitle( 0, str, FONT12POINT1, FONT_BLACK, FONT_BLACK );
	//#endif
	if( !LoadWorld(bFilename) )
	{
		return( FALSE );
	}

	// underground?
	if( bSectorZ )
	{
		pNode = FindUnderGroundSector( sSectorX, sSectorY, bSectorZ );

		// is there a sector?..if so set flag
		if( pNode )
		{
			pNode->fVisited = TRUE;
		}
	}

	// if we arent loading a saved game
	// ATE: Moved this form above, so that we can have the benefit of
	// changing the world BEFORE adding guys to it...
	if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
	{
		// Load the current sectors Information From the temporary files
		if( !LoadCurrentSectorsInformationFromTempItemsFile() )
		{ //The integrity of the temp files have been compromised.  Boot out of the game after warning message.
			InitExitGameDialogBecauseFileHackDetected();
			return TRUE;
		}
	}

	RemoveLoadingScreenProgressBar();
	//RemoveProgressBar( 0 );

	if( gfEnterTacticalPlacementGUI )
	{
		SetPendingNewScreen(GAME_SCREEN);
		InitTacticalPlacementGUI();
	}
	else
	{
		PrepareLoadedSector();
	}

//	UnPauseGame( );

	// This function will either hide or display the tree tops, depending on the game setting
	SetTreeTopStateForMap();

	return TRUE; //because the map was loaded.
}


void UpdateMercsInSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ )
{
	INT32					cnt;
	SOLDIERTYPE		*pSoldier;
	BOOLEAN				fPOWSquadSet = FALSE;
	UINT8					ubPOWSquad=0;

	// Remove from interface slot
	RemoveAllPlayersFromSlot( );

	// Remove tactical interface stuff
	guiPendingOverrideEvent = I_CHANGE_TO_IDLE;

	//If we are in this function during the loading of a sector
	if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
	{
		//DONT set these values
		gusSelectedSoldier = NOBODY;
		gfGameScreenLocateToSoldier = TRUE;
	}

	// Set all faces intactive
	SetAllAutoFacesInactive( );

	if ( fUsingEdgePointsForStrategicEntry )
	{
		BeginMapEdgepointSearch();
	}

	//if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
	{
		for ( cnt = 0, pSoldier = MercPtrs[ cnt ]; cnt < MAX_NUM_SOLDIERS; cnt++, pSoldier++)
		{
			if ( gfRestoringEnemySoldiersFromTempFile &&
					cnt >= gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID &&
					cnt <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID )
			{ //Don't update enemies/creatures (consec. teams) if they were
				//just restored via the temp map files...
				continue;
			}
			// Remove old merc, if exists
			RemoveMercSlot( pSoldier );

			pSoldier->bInSector = FALSE;

			if ( pSoldier->bActive  )
			{
				if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
				{
					if( gMapInformation.sCenterGridNo != -1 && gfBlitBattleSectorLocator &&
							(gubEnemyEncounterCode == ENEMY_AMBUSH_CODE || gubEnemyEncounterCode == BLOODCAT_AMBUSH_CODE) && pSoldier->bTeam != CIV_TEAM )
					{
						pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
						pSoldier->usStrategicInsertionData = gMapInformation.sCenterGridNo;
					}
					else if( gfOverrideInsertionWithExitGrid )
					{
						pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
						pSoldier->usStrategicInsertionData = gExitGrid.usGridNo;
					}
				}

				if ( pSoldier->sSectorX == sSectorX && pSoldier->sSectorY == sSectorY && pSoldier->bSectorZ == bSectorZ && !pSoldier->flags.fBetweenSectors )
				{
					gbMercIsNewInThisSector[ pSoldier->ubID ] = 1;

					UpdateMercInSector( pSoldier, sSectorX, sSectorY, bSectorZ );

					if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
					{
						if ( pSoldier->bAssignment == ASSIGNMENT_POW )
						{
							if ( !fPOWSquadSet )
							{
								fPOWSquadSet = TRUE;

								// ATE: If we are in i13 - pop up message!
								if ( sSectorY == MAP_ROW_I && sSectorX == 13 )
								{
									DoMessageBox( MSG_BOX_BASIC_STYLE, TacticalStr[ POW_MERCS_ARE_HERE ], GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_OK, NULL, NULL );
								}
								else
								{
									AddCharacterToUniqueSquad( pSoldier );
									ubPOWSquad = pSoldier->bAssignment;
									pSoldier->aiData.bNeutral		= FALSE;
								}
							}
							else
							{
								if ( sSectorY != MAP_ROW_I && sSectorX != 13 )
								{
									AddCharacterToSquad( pSoldier, ubPOWSquad );
								}
							}

							// ATE: Call actions based on what POW we are on...
							if ( gubQuest[ QUEST_HELD_IN_ALMA ] == QUESTINPROGRESS )
							{
								// Complete quest
								EndQuest( QUEST_HELD_IN_ALMA, sSectorX, sSectorY );

								// Do action
								HandleNPCDoAction( 0, NPC_ACTION_GRANT_EXPERIENCE_3, 0 );

							}

						}
					}
				}
				else
				{
					pSoldier->bInSector = FALSE;
				}
			}
		}
	}

	if ( fUsingEdgePointsForStrategicEntry )
	{
		EndMapEdgepointSearch();

		// Set to false
		fUsingEdgePointsForStrategicEntry = FALSE;
	}

}

void UpdateMercInSector( SOLDIERTYPE *pSoldier, INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ )
{
	BOOLEAN fError = FALSE;
	if( pSoldier->flags.uiStatusFlags & SOLDIER_IS_TACTICALLY_VALID)
	{
		pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
	}
	// OK, determine entrence direction and get sweetspot
	// Only if we are an OK guy to control....
	// SOME CHECKS HERE MUST BE FLESHED OUT......
	if ( pSoldier->bActive )		// This was in the if, removed by DEF:  pSoldier->stats.bLife >= OKLIFE &&
	{
		// If we are not in transit...
		if ( pSoldier->bAssignment != IN_TRANSIT )
		{
			// CHECK UBINSERTION CODE..
			if( pSoldier->ubStrategicInsertionCode == INSERTION_CODE_PRIMARY_EDGEINDEX ||
					pSoldier->ubStrategicInsertionCode == INSERTION_CODE_SECONDARY_EDGEINDEX )
			{
				if ( !fUsingEdgePointsForStrategicEntry )
				{
					// If we are not supposed to use this now, pick something better...
					pSoldier->ubStrategicInsertionCode = (UINT8)pSoldier->usStrategicInsertionData;
				}
			}

			MAPEDGEPOINT_SEARCH_FAILED:

			if ( pSoldier->ubProfile != NO_PROFILE && gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags3 & PROFILE_MISC_FLAG3_PERMANENT_INSERTION_CODE )
			{
				// override orders
				pSoldier->aiData.bOrders = STATIONARY;
			}


			// Use insertion direction from loaded map!
			switch( pSoldier->ubStrategicInsertionCode )
			{
				case INSERTION_CODE_NORTH:
					pSoldier->sInsertionGridNo = gMapInformation.sNorthGridNo;
					if ( !gfEditMode && gMapInformation.sNorthGridNo == -1 )
						fError = TRUE;
					break;
				case INSERTION_CODE_SOUTH:
					pSoldier->sInsertionGridNo = gMapInformation.sSouthGridNo;
					if ( !gfEditMode && gMapInformation.sSouthGridNo == -1)
						fError = TRUE;
					break;
				case INSERTION_CODE_EAST:
					pSoldier->sInsertionGridNo = gMapInformation.sEastGridNo;
					if ( !gfEditMode && gMapInformation.sEastGridNo == -1)
						fError = TRUE;
					break;
				case INSERTION_CODE_WEST:
					pSoldier->sInsertionGridNo = gMapInformation.sWestGridNo;
					if ( !gfEditMode && gMapInformation.sWestGridNo == -1)
						fError = TRUE;
					break;
				case INSERTION_CODE_CENTER:
					pSoldier->sInsertionGridNo = gMapInformation.sCenterGridNo;
					if ( !gfEditMode && gMapInformation.sCenterGridNo == -1)
						fError = TRUE;
					break;
				case INSERTION_CODE_GRIDNO:
					pSoldier->sInsertionGridNo = pSoldier->usStrategicInsertionData;
					break;

				case INSERTION_CODE_PRIMARY_EDGEINDEX:
					//dnl ch48 041009 Risky translation, must never occur again on same element
					gMapTrn.ResizeTrnCnt(pSoldier->aiData.sPendingActionData2);
					if(fUsingEdgePointsForStrategicEntry)
					{
						INT32 sWorldX, sWorldY;
						INT16 sScreenX, sScreenY, sX, sY;
						// Determine 'mirror' gridno...
						ConvertGridNoToXY(pSoldier->aiData.sPendingActionData2, &sX, &sY);
						// Convert to absolute xy
						GetWorldXYAbsoluteScreenXY(sX, sY, &sScreenX, &sScreenY);
						// Get 'mirror', depending on what direction...
						switch(gubTacticalDirection)
						{
						case NORTH:
							sScreenY = MAPHEIGHT;
							break;
						case SOUTH:
							sScreenY = 0;
							break;
						case EAST:
							sScreenX = 0;
							break;
						case WEST:
							sScreenX = MAPWIDTH;
							break;
						}
						// Convert into a gridno again.....
						GetFromAbsoluteScreenXYWorldXY(&sWorldX, &sWorldY, sScreenX, sScreenY);
						// Save this gridNo....
						pSoldier->aiData.sPendingActionData2 = GETWORLDINDEXFROMWORLDCOORDS(sWorldY, sWorldX);
					}
					pSoldier->sInsertionGridNo = SearchForClosestPrimaryMapEdgepoint(pSoldier->aiData.sPendingActionData2, (UINT8)pSoldier->usStrategicInsertionData);
					#ifdef JA2BETAVERSION
					{
						CHAR8 str[256];
						sprintf( str, "%S's primary insertion gridno is %d using %d as initial search gridno and %d insertion code.",
													pSoldier->name, pSoldier->sInsertionGridNo, pSoldier->aiData.sPendingActionData2, pSoldier->usStrategicInsertionData );
						DebugMsg( TOPIC_JA2, DBG_LEVEL_3, str );
					}
					#endif					
					if( TileIsOutOfBounds(pSoldier->sInsertionGridNo))
					{
						ScreenMsg( FONT_RED, MSG_ERROR, L"Main edgepoint search failed for %s -- substituting entrypoint.", pSoldier->name );
						pSoldier->ubStrategicInsertionCode = (UINT8)pSoldier->usStrategicInsertionData;
						goto MAPEDGEPOINT_SEARCH_FAILED;
					}
					break;
				case INSERTION_CODE_SECONDARY_EDGEINDEX:
					pSoldier->sInsertionGridNo = SearchForClosestSecondaryMapEdgepoint( pSoldier->aiData.sPendingActionData2, (UINT8)pSoldier->usStrategicInsertionData );
					#ifdef JA2BETAVERSION
					{
						CHAR8 str[256];
						sprintf( str, "%S's isolated insertion gridno is %d using %d as initial search gridno and %d insertion code.",
													pSoldier->name, pSoldier->sInsertionGridNo, pSoldier->aiData.sPendingActionData2, pSoldier->usStrategicInsertionData );
						DebugMsg( TOPIC_JA2, DBG_LEVEL_3, str );
					}
					#endif					
					if(TileIsOutOfBounds(pSoldier->sInsertionGridNo))
					{
						ScreenMsg( FONT_RED, MSG_ERROR, L"Isolated edgepont search failed for %s -- substituting entrypoint.", pSoldier->name );
						pSoldier->ubStrategicInsertionCode = (UINT8)pSoldier->usStrategicInsertionData;
						goto MAPEDGEPOINT_SEARCH_FAILED;
					}
					break;

				case INSERTION_CODE_ARRIVING_GAME:
					// Are we in Omerta!
					if ( sSectorX == gWorldSectorX && gWorldSectorX == 9 && sSectorY == gWorldSectorY && gWorldSectorY == 1 && bSectorZ == gbWorldSectorZ && gbWorldSectorZ == 0 )
					{
						// TODO.WANNE: Hardcoded grid number
						// Try another location and walk into map
						pSoldier->sInsertionGridNo = 4379;//dnl!!!
					}
					else
					{
					#ifdef JA2UB
						//pSoldier->ubStrategicInsertionCode = INSERTION_CODE_NORTH;
						//pSoldier->sInsertionGridNo		   = gMapInformation.sNorthGridNo;
						pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
						pSoldier->sInsertionGridNo		   = gGameLegionOptions.LOCATEGRIDNO;
					#else
						//pSoldier->ubStrategicInsertionCode = INSERTION_CODE_NORTH;
						//pSoldier->sInsertionGridNo				 = gMapInformation.sNorthGridNo;
						pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
						pSoldier->sInsertionGridNo = gGameExternalOptions.iInitialMercArrivalLocation;
					#endif
					}
					break;
				case INSERTION_CODE_CHOPPER:
					// Try another location and walk into map
					// Add merc to chopper....
					//pSoldier->sInsertionGridNo = 4058;
					AddMercToHeli( pSoldier->ubID );
					return;
					break;
				default:
					// TODO.WANNE: Hardcoded grid number
					pSoldier->sInsertionGridNo = 12880;//dnl!!!
					DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Improper insertion code %d given to UpdateMercsInSector", pSoldier->ubStrategicInsertionCode ) );
					break;
			}

			if( fError )
			{ //strategic insertion failed because it expected to find an entry point.  This is likely
				//a missing part of the map or possible fault in strategic movement costs, traversal logic, etc.
				CHAR16 szEntry[10];
				CHAR16 szSector[10];
				INT32 sGridNo;
				GetLoadedSectorString( szSector );
				if( gMapInformation.sNorthGridNo != -1 )
				{
					swprintf( szEntry, L"north" );
					sGridNo = gMapInformation.sNorthGridNo;
				}
				else if( gMapInformation.sEastGridNo != -1 )
				{
					swprintf( szEntry, L"east" );
					sGridNo = gMapInformation.sEastGridNo;
				}
				else if( gMapInformation.sSouthGridNo != -1 )
				{
					swprintf( szEntry, L"south" );
					sGridNo = gMapInformation.sSouthGridNo;
				}
				else if( gMapInformation.sWestGridNo != -1 )
				{
					swprintf( szEntry, L"west" );
					sGridNo = gMapInformation.sWestGridNo;
				}
				else if( gMapInformation.sCenterGridNo != -1 )
				{
					swprintf( szEntry, L"center" );
					sGridNo = gMapInformation.sCenterGridNo;
				}
				else
				{
					ScreenMsg( FONT_RED, MSG_BETAVERSION, L"Sector %s has NO entrypoints -- using precise center of map for %s.", szSector, pSoldier->name );
					pSoldier->sInsertionGridNo = 12880;//dnl!!!
					AddSoldierToSector( pSoldier->ubID );
					return;
				}
				pSoldier->sInsertionGridNo = sGridNo;
				switch( pSoldier->ubStrategicInsertionCode )
				{
					case INSERTION_CODE_NORTH:
						ScreenMsg( FONT_RED, MSG_BETAVERSION, L"Sector %s doesn't have a north entrypoint -- substituting  %s entrypoint for %s.", szSector, szEntry, pSoldier->name );
						break;
					case INSERTION_CODE_EAST:
						ScreenMsg( FONT_RED, MSG_BETAVERSION, L"Sector %s doesn't have a east entrypoint -- substituting  %s entrypoint for %s.", szSector, szEntry, pSoldier->name );
						break;
					case INSERTION_CODE_SOUTH:
						ScreenMsg( FONT_RED, MSG_BETAVERSION, L"Sector %s doesn't have a south entrypoint -- substituting  %s entrypoint for %s.", szSector, szEntry, pSoldier->name );
						break;
					case INSERTION_CODE_WEST:
						ScreenMsg( FONT_RED, MSG_BETAVERSION, L"Sector %s doesn't have a west entrypoint -- substituting  %s entrypoint for %s.", szSector, szEntry, pSoldier->name );
						break;
					case INSERTION_CODE_CENTER:
						ScreenMsg( FONT_RED, MSG_BETAVERSION, L"Sector %s doesn't have a center entrypoint -- substituting  %s entrypoint for %s.", szSector, szEntry, pSoldier->name );
						break;
				}
			}
			// If no insertion direction exists, this is bad!
			if ( pSoldier->sInsertionGridNo == -1 )
			{
				DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Insertion gridno for direction %d not added to map sector %d %d", pSoldier->ubStrategicInsertionCode, sSectorX, sSectorY ) );
				pSoldier->sInsertionGridNo = 12880;//dnl!!!
			}

			AddSoldierToSector( pSoldier->ubID );
		}
	}
}

// Get sector ID string makes a string like 'A9 - OMERTA', or just J11 if no town....
void GetSectorIDString( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ , STR16 zString, BOOLEAN fDetailed )
{
	SECTORINFO *pSector = NULL;
	UNDERGROUND_SECTORINFO *pUnderground;
	INT8		bTownNameID;
	UINT8 ubSectorID = 0;
	UINT8 ubLandType = 0;

	if( sSectorX <= 0 || sSectorY <= 0 || bSectorZ < 0 )
	{
		//swprintf( zString, L"%s", pErrorStrings[0] );
	}
	else if( bSectorZ != 0 ) // UNDERGROUND SECTORS
	{
		pUnderground = FindUnderGroundSector( sSectorX, sSectorY, bSectorZ );

		#if 0 // reading sector names from lua now
		////////////////////////////////////
		// Read and verify XML sector names
		
		INT8		bMineIndex;
		ubSectorID = (UINT8)SECTOR( sSectorX, sSectorY );
		AssertGE(ubSectorID, 0);
		AssertLT(ubSectorID,256);
		pSector = &SectorInfo[ ubSectorID ];
		ubLandType = pSector->ubTraversability[ 4 ];
		swprintf( zString, L"%c%d: ", 'A' + sSectorY - 1, sSectorX );
		
		BOOLEAN fSectorHasXMLNames = TRUE;
		CHAR16 zUnexploredUnderground[MAX_SECTOR_NAME_LENGTH];
		CHAR16 zDetailedUnexploredUnderground[MAX_SECTOR_NAME_LENGTH];
		CHAR16 zExploredUnderground[MAX_SECTOR_NAME_LENGTH];
		CHAR16 zDetailedExploredUnderground[MAX_SECTOR_NAME_LENGTH];	
	
		if ( bSectorZ == 1 )
		{		
			wcscpy( zUnexploredUnderground, gzSectorUndergroundNames1[ ubSectorID ][0] );
			wcscpy( zDetailedUnexploredUnderground, gzSectorUndergroundNames1[ ubSectorID ][1] );
			wcscpy( zExploredUnderground, gzSectorUndergroundNames1[ ubSectorID ][2] );
			wcscpy( zDetailedExploredUnderground, gzSectorUndergroundNames1[ ubSectorID ][3] );
		}
		else if ( bSectorZ == 2 )
		{
			wcscpy( zUnexploredUnderground, gzSectorUndergroundNames2[ ubSectorID ][0] );
			wcscpy( zDetailedUnexploredUnderground, gzSectorUndergroundNames2[ ubSectorID ][1] );
			wcscpy( zExploredUnderground, gzSectorUndergroundNames2[ ubSectorID ][2] );
			wcscpy( zDetailedExploredUnderground, gzSectorUndergroundNames2[ ubSectorID ][3] );
		}
		else if ( bSectorZ == 3 )
		{
			wcscpy( zUnexploredUnderground, gzSectorUndergroundNames3[ ubSectorID ][0] );
			wcscpy( zDetailedUnexploredUnderground, gzSectorUndergroundNames3[ ubSectorID ][1] );
			wcscpy( zExploredUnderground, gzSectorUndergroundNames3[ ubSectorID ][2] );
			wcscpy( zDetailedExploredUnderground, gzSectorUndergroundNames3[ ubSectorID ][3] );
		}
	
		if (zUnexploredUnderground[0] == 0 || zDetailedUnexploredUnderground[0] == 0 || zExploredUnderground[0] == 0 || zDetailedExploredUnderground[0] == 0)
		{
			fSectorHasXMLNames = FALSE;
		}

		if (fSectorHasXMLNames) // UNDERGROUND XML
		{
			if ( pUnderground )
			{
				bMineIndex = GetIdOfMineForSector( sSectorX, sSectorY, bSectorZ );
				if( bMineIndex != -1 )
				{
					swprintf( zString, L"%c%d: %s %s", 'A' + sSectorY - 1, sSectorX, pTownNames[ GetTownAssociatedWithMine( bMineIndex ) ], pwMineStrings[ 0 ] );
				}	
				else if ( pUnderground->fVisited )
				{
					if (fDetailed)
					{			
						wcscat( zString, zDetailedExploredUnderground );	
					}
					else
					{
						wcscat( zString, zExploredUnderground );
					}
				}
				else
				{
					if (fDetailed)
					{
						wcscat( zString, zDetailedUnexploredUnderground );
					}
					else
					{
						wcscat( zString, zUnexploredUnderground );
					}
				}

				if (pUnderground->ubNumCreatures)
					swprintf( zString, L"%c%d: %s", 'A' + sSectorY - 1, sSectorX, pLandTypeStrings[ CREATURE_LAIR ] );
				//else
				//	swprintf( zString, L"%c%d-%d", 'A' + sSectorY - 1, sSectorX, bSectorZ );
			}		
		}
		else // UNDERGROUND HARDCODED
		{	
			pUnderground = FindUnderGroundSector( sSectorX, sSectorY, bSectorZ );
			if( pUnderground && ( pUnderground->fVisited || gfGettingNameFromSaveLoadScreen ) )
			{
				bMineIndex = GetIdOfMineForSector( sSectorX, sSectorY, bSectorZ );
				if( bMineIndex != -1 )
				{
					swprintf( zString, L"%c%d: %s %s", 'A' + sSectorY - 1, sSectorX, pTownNames[ GetTownAssociatedWithMine( bMineIndex ) ], pwMineStrings[ 0 ] );
				}
				else switch( SECTOR( sSectorX, sSectorY ) )
				{
					case SEC_A10:
						swprintf( zString, L"A10: %s", pLandTypeStrings[ REBEL_HIDEOUT ] );
						break;
					case SEC_J9:
						swprintf( zString, L"J9: %s", pLandTypeStrings[ TIXA_DUNGEON ] );
						break;
					case SEC_K4:
						swprintf( zString, L"K4: %s", pLandTypeStrings[ ORTA_BASEMENT ] );
						break;
					case SEC_O3:
						swprintf( zString, L"O3: %s", pLandTypeStrings[ TUNNEL ] );
						break;
					case SEC_P3:
						swprintf( zString, L"P3: %s", pLandTypeStrings[ SHELTER ] );
						break;
					default:
						if (pUnderground->ubNumCreatures)
							swprintf( zString, L"%c%d: %s", 'A' + sSectorY - 1, sSectorX, pLandTypeStrings[ CREATURE_LAIR ] );
						else
							swprintf( zString, L"%c%d-%d", 'A' + sSectorY - 1, sSectorX, bSectorZ );
						break;
				}
			}
			else
			{ //Display nothing
				wcscpy( zString, L"" );
			}
		}
	#else

		// TODO: the code is riddled with (potential) buffer overruns
		// this totally needs to be fixed sometime
		// anyway, only trying to not create another one here
		// found calls to this function with buffers as small as 50 elements
		g_luaUnderground.GetSectorName(sSectorX, sSectorY, bSectorZ, pUnderground, zString, 50, fDetailed);

	#endif
	}
	else // SECTORS ABOVE GROUND
	{

		bTownNameID = StrategicMap[ CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY ) ].bNameId;
		ubSectorID = (UINT8)SECTOR( sSectorX, sSectorY );
		AssertGE(ubSectorID, 0);
		AssertLT(ubSectorID,256);
		pSector = &SectorInfo[ ubSectorID ];
		ubLandType = pSector->ubTraversability[ 4 ];
		swprintf( zString, L"%c%d: ", 'A' + sSectorY - 1, sSectorX );

		////////////////////////////////////
		// Read and verify XML sector names
		BOOLEAN fSectorHasXMLNames = TRUE;
		CHAR16 zUnexplored[MAX_SECTOR_NAME_LENGTH];
		CHAR16 zDetailedUnexplored[MAX_SECTOR_NAME_LENGTH];
		CHAR16 zExplored[MAX_SECTOR_NAME_LENGTH];
		CHAR16 zDetailedExplored[MAX_SECTOR_NAME_LENGTH];
		
		wcscpy( zUnexplored, gzSectorNames[ ubSectorID ][0] );
		wcscpy( zDetailedUnexplored, gzSectorNames[ ubSectorID ][1] );
		wcscpy( zExplored, gzSectorNames[ ubSectorID ][2] );
		wcscpy( zDetailedExplored, gzSectorNames[ ubSectorID ][3] );

		if (zUnexplored[0] == 0 || zDetailedUnexplored[0] == 0 || zExplored[0] == 0 || zDetailedExplored[0] == 0)
		{
			fSectorHasXMLNames = FALSE;
		}

		if (fSectorHasXMLNames) // ABOVE GROUND XML
		{
			// HEADROCK HAM 3.6: The program can now read custom names from XML for all above-ground sectors.
			// In the event that a specific name or set of names is missing, the program generates a default
			// name as it always has.
			// I've also updated the SAM Site sectors to rely on SamSite.XML data.
			
			BOOLEAN fVisited = (SectorInfo[ ubSectorID ].uiFlags & SF_ALREADY_VISITED);
			BOOLEAN fSAMSiteKnown = FALSE;

			// Test for known SAM Site at this location
			for (UINT16 x=0; x < MAX_NUMBER_OF_SAMS; x++)
			{
				if ( pSamList[x] == ubSectorID )
				{
					if ( fSamSiteFound[ x ] )
					{
						fSAMSiteKnown = TRUE;
					}
				}
			}

			if (fVisited || fSAMSiteKnown)
			{
				if (fDetailed)
				{
					wcscat( zString, zDetailedExplored );
				}
				else
				{
					wcscat( zString, zExplored);
				}
			}
			else
			{
				if (fDetailed)
				{
					wcscat( zString, zDetailedUnexplored);
				}
				else
				{
					wcscat( zString, zUnexplored );
				}
			}
		}
		else // ABOVE GROUND HARDCODED
		{
			// Default JA2 Name selection
			if ( bTownNameID == BLANK_SECTOR )
			{
				// OK, build string id like J11
				// are we dealing with the unfound towns?
				switch( ubSectorID )
				{
					case SEC_D2: //Chitzena SAM
						if( !fSamSiteFound[ SAM_SITE_ONE ] )
							wcscat( zString, pLandTypeStrings[ TROPICS ] );
						else if( fDetailed )
							wcscat( zString, pLandTypeStrings[ TROPICS_SAM_SITE ] );
						else
							wcscat( zString, pLandTypeStrings[ SAM_SITE ] );
						break;
					case SEC_D15: //Drassen SAM
						if( !fSamSiteFound[ SAM_SITE_TWO ] )
							wcscat( zString, pLandTypeStrings[ SPARSE ] );
						else if( fDetailed )
							wcscat( zString, pLandTypeStrings[ SPARSE_SAM_SITE ] );
						else
							wcscat( zString, pLandTypeStrings[ SAM_SITE ] );
						break;
					case SEC_I8: //Cambria SAM
						if( !fSamSiteFound[ SAM_SITE_THREE ] )
							wcscat( zString, pLandTypeStrings[ SAND ] );
						else if( fDetailed )
							wcscat( zString, pLandTypeStrings[ SAND_SAM_SITE ] );
						else
							wcscat( zString, pLandTypeStrings[ SAM_SITE ] );
						break;
					default:
						wcscat( zString, pLandTypeStrings[ ubLandType ] );
						break;
				}
			}
			else
			{
				switch( ubSectorID )
				{
					case SEC_B13:
						if( fDetailed )
							wcscat( zString, pLandTypeStrings[ DRASSEN_AIRPORT_SITE ] );
						else
							wcscat( zString, pTownNames[ DRASSEN ] );
						break;
					case SEC_F8:
						if( fDetailed )
							wcscat( zString, pLandTypeStrings[ CAMBRIA_HOSPITAL_SITE ] );
						else
							wcscat( zString, pTownNames[ CAMBRIA ] );
						break;
					case SEC_J9: //Tixa
						//if( !fFoundTixa )
						if( gfHiddenTown[ TIXA ] == FALSE )
							wcscat( zString, pLandTypeStrings[ SAND ] );
						else
							wcscat( zString, pTownNames[ TIXA ] );
						break;
					case SEC_K4: //Orta
						//if( !fFoundOrta )
						if( gfHiddenTown[ ORTA ] == FALSE )
							wcscat( zString, pLandTypeStrings[ SWAMP ] );
						else
							wcscat( zString, pTownNames[ ORTA ] );
						break;
					case SEC_N3:
						if( fDetailed )
							wcscat( zString, pLandTypeStrings[ MEDUNA_AIRPORT_SITE ] );
						else
							wcscat( zString, pTownNames[ MEDUNA ] );
						break;
					default:
						if( ubSectorID == SEC_N4 && fSamSiteFound[ SAM_SITE_FOUR ] )
						{	//Meduna's SAM site
							if( fDetailed )
								wcscat( zString, pLandTypeStrings[ MEDUNA_SAM_SITE ] );
							else
								wcscat( zString, pLandTypeStrings[ SAM_SITE ] );
						}
						else
						{	//All other towns that are known since beginning of the game.
							wcscat( zString, pTownNames[ bTownNameID ] );
							if( fDetailed )
							{
								switch( ubSectorID )
								{ //Append the word, "mine" for town sectors containing a mine.
									case SEC_B2:
									case SEC_D4:
									case SEC_D13:
									case SEC_H3:
									case SEC_H8:
									case SEC_I14:
										wcscat( zString, L" " ); //space
										wcscat( zString, pwMineStrings[ 0 ] ); //then "Mine"
										break;
								}
							}
						}
						break;
				}
			}
		}
	}
}


UINT8 SetInsertionDataFromAdjacentMoveDirection( SOLDIERTYPE *pSoldier, UINT8 ubTacticalDirection, INT32 sAdditionalData  )//dnl ch56 151009
{
	UINT8				ubDirection;
	EXITGRID		ExitGrid;


	// Set insertion code
	switch( ubTacticalDirection )
	{
		// OK, we are using an exit grid - set insertion values...

		case 255:
			if ( !GetExitGrid( sAdditionalData, &ExitGrid ) )
			{
				AssertMsg( 0, String( "No valid Exit grid can be found when one was expected: SetInsertionDataFromAdjacentMoveDirection." ) );
			}
			ubDirection = 255;
			pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
			pSoldier->usStrategicInsertionData = ExitGrid.usGridNo;
			pSoldier->bUseExitGridForReentryDirection = TRUE;
			break;

		case NORTH:
			ubDirection = NORTH_STRATEGIC_MOVE;
			pSoldier->ubStrategicInsertionCode = INSERTION_CODE_SOUTH;
			break;
		case SOUTH:
			ubDirection = SOUTH_STRATEGIC_MOVE;
			pSoldier->ubStrategicInsertionCode = INSERTION_CODE_NORTH;
			break;
		case EAST:
			ubDirection = EAST_STRATEGIC_MOVE;
			pSoldier->ubStrategicInsertionCode = INSERTION_CODE_WEST;
			break;
		case WEST:
			ubDirection = WEST_STRATEGIC_MOVE;
			pSoldier->ubStrategicInsertionCode = INSERTION_CODE_EAST;
			break;
		default:
			// Wrong direction given!
			#ifdef JA2BETAVERSION
				DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Improper insertion direction %d given to SetInsertionDataFromAdjacentMoveDirection", ubTacticalDirection ) );
				ScreenMsg( FONT_RED, MSG_ERROR, L"Improper insertion direction %d given to SetInsertionDataFromAdjacentMoveDirection", ubTacticalDirection );
			#endif
			ubDirection = EAST_STRATEGIC_MOVE;
			pSoldier->ubStrategicInsertionCode = INSERTION_CODE_WEST;
	}

	return( ubDirection );

}

UINT8 GetInsertionDataFromAdjacentMoveDirection( UINT8 ubTacticalDirection, INT32 sAdditionalData  )//dnl ch56 151009
{
	UINT8				ubDirection;


	// Set insertion code
	switch( ubTacticalDirection )
	{
		// OK, we are using an exit grid - set insertion values...

		case 255:

			ubDirection = 255;
			break;

		case NORTH:
			ubDirection = NORTH_STRATEGIC_MOVE;
			break;
		case SOUTH:
			ubDirection = SOUTH_STRATEGIC_MOVE;
			break;
		case EAST:
			ubDirection = EAST_STRATEGIC_MOVE;
			break;
		case WEST:
			ubDirection = WEST_STRATEGIC_MOVE;
			break;
		default:
			// Wrong direction given!
			#ifdef JA2BETAVERSION
				DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Improper insertion direction %d given to GetInsertionDataFromAdjacentMoveDirection", ubTacticalDirection ) );
				ScreenMsg( FONT_RED, MSG_ERROR, L"Improper insertion direction %d given to GetInsertionDataFromAdjacentMoveDirection", ubTacticalDirection );
			#endif
			ubDirection = EAST_STRATEGIC_MOVE;
	}

	return( ubDirection );

}

UINT8 GetStrategicInsertionDataFromAdjacentMoveDirection( UINT8 ubTacticalDirection, INT32 sAdditionalData  )//dnl ch56 151009
{
	UINT8				ubDirection;


	// Set insertion code
	switch( ubTacticalDirection )
	{
		// OK, we are using an exit grid - set insertion values...

		case 255:

			ubDirection = 255;
			break;

		case NORTH:
			ubDirection = INSERTION_CODE_SOUTH;
			break;
		case SOUTH:
			ubDirection = INSERTION_CODE_NORTH;
			break;
		case EAST:
			ubDirection = INSERTION_CODE_WEST;
			break;
		case WEST:
			ubDirection = INSERTION_CODE_EAST;
			break;
		default:
			// Wrong direction given!
			#ifdef JA2BETAVERSION
				DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Improper insertion direction %d given to SetInsertionDataFromAdjacentMoveDirection", ubTacticalDirection ) );
				ScreenMsg( FONT_RED, MSG_ERROR, L"Improper insertion direction %d given to GetStrategicInsertionDataFromAdjacentMoveDirection", ubTacticalDirection );
			#endif
			ubDirection = EAST_STRATEGIC_MOVE;
	}

	return( ubDirection );

}


void JumpIntoAdjacentSector( UINT8 ubTacticalDirection, UINT8 ubJumpCode, INT32 sAdditionalData )//dnl ch56 151009
{
	INT32 cnt;
	SOLDIERTYPE		*pSoldier;
	SOLDIERTYPE *pValidSoldier = NULL;
	GROUP *pGroup;
	UINT32 uiTraverseTime=0;
	UINT8 ubDirection = 0xff;
	EXITGRID ExitGrid;
	INT8 bPrevAssignment;
	UINT8 ubPrevGroupID;

	// Set initial selected
	// ATE: moved this towards top...
	gubPreferredInitialSelectedGuy = (UINT8)gusSelectedSoldier;

	if ( ubJumpCode == JUMP_ALL_LOAD_NEW || ubJumpCode == JUMP_ALL_NO_LOAD )
	{
		// TODO: Check flags to see if we can jump!
		// Move controllable mercs!
		cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;

		// look for all mercs on the same team,
		for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++)
		{
			// If we are controllable
			if ( OK_CONTROLLABLE_MERC( pSoldier) && pSoldier->bAssignment == CurrentSquad( )  )
			{
				pValidSoldier = pSoldier;
				//This now gets handled by strategic movement.  It is possible that the
				//group won't move instantaneously.
				//pSoldier->sSectorX = sNewX;
				//pSoldier->sSectorY = sNewY;

				ubDirection = GetInsertionDataFromAdjacentMoveDirection( ubTacticalDirection, sAdditionalData );
				break;
			}
		}
	}
	else if ( ( ubJumpCode == JUMP_SINGLE_LOAD_NEW || ubJumpCode == JUMP_SINGLE_NO_LOAD ) )
	{
		// Use selected soldier...
		// This guy should always be 1 ) selected and 2 ) close enough to exit sector to leave
		if ( gusSelectedSoldier != NOBODY )
		{
			pValidSoldier = MercPtrs[ gusSelectedSoldier ];
			ubDirection = GetInsertionDataFromAdjacentMoveDirection( ubTacticalDirection, sAdditionalData );
		}

		// save info for desired squad and and time for all single mercs leaving their squad.
		bPrevAssignment = pValidSoldier->bAssignment;
		ubPrevGroupID = pValidSoldier->ubGroupID;

		if( ubJumpCode == JUMP_SINGLE_NO_LOAD )
		{ // handle soldier moving by themselves
			HandleSoldierLeavingSectorByThemSelf( pValidSoldier );
		}
		else
		{ // now add char to a squad all their own
			AddCharacterToUniqueSquad( pValidSoldier );
		}
		if( !pValidSoldier->ubNumTraversalsAllowedToMerge && bPrevAssignment < ON_DUTY )
		{
			PLAYERGROUP *pPlayer;
			pValidSoldier->ubDesiredSquadAssignment = bPrevAssignment;
			pValidSoldier->ubNumTraversalsAllowedToMerge = 2;
			pGroup = GetGroup( ubPrevGroupID );
			Assert( pGroup );
			Assert( pGroup->fPlayer );
			Assert( pGroup->ubGroupSize );
			pPlayer = pGroup->pPlayerList;
			while( pPlayer )
			{
				if( pPlayer->pSoldier != pValidSoldier )
				{
					pPlayer->pSoldier->ubNumTraversalsAllowedToMerge = 100;
					pPlayer->pSoldier->ubDesiredSquadAssignment = NO_ASSIGNMENT;
				}
				pPlayer = pPlayer->next;
			}
		}
	}
	else
	{
		// OK, no jump code here given...
		DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Improper jump code %d given to JumpIntoAdjacentSector", ubJumpCode ) );
	}

	Assert( pValidSoldier );


	//Now, determine the traversal time.
	pGroup = GetGroup( pValidSoldier->ubGroupID );
	AssertMsg( pGroup, String( "%S is not in a valid group (pSoldier->ubGroupID is %d)",pValidSoldier->name, pValidSoldier->ubGroupID) );

	// If we are going through an exit grid, don't get traversal direction!
	if ( ubTacticalDirection != 255 )
	{
		if( !gbWorldSectorZ )
		{
			uiTraverseTime = GetSectorMvtTimeForGroup( (UINT8)SECTOR( pGroup->ubSectorX, pGroup->ubSectorY ), ubDirection, pGroup );
		}
		else if( gbWorldSectorZ > 0 )
		{ //We are attempting to traverse in an underground environment.  We need to use a complete different
			//method.  When underground, all sectors are instantly adjacent.
			uiTraverseTime = UndergroundTacticalTraversalTime( ubDirection );
		}
		if( uiTraverseTime == 0xffffffff )
			AssertMsg( 0, "Attempting to tactically traverse to adjacent sector, despite being unable to do so.");
	}

	// Alrighty, we want to do whatever our omnipotent player asked us to do
	// this is what the ubJumpCode is for.
	// Regardless of that we were asked to do, we MUST walk OFF ( Ian loves this... )
	// So..... let's setup our people to walk off...
	// We deal with a pGroup here... if an all move or a group...

	// Setup some globals so our callback that deals when guys go off screen is handled....
	// Look in the handler function AllMercsHaveWalkedOffSector() below...
	gpAdjacentGroup				= pGroup;
	gubAdjacentJumpCode		= ubJumpCode;
	guiAdjacentTraverseTime	= uiTraverseTime;
	gubTacticalDirection  = ubTacticalDirection;
	gsAdditionalData			= sAdditionalData;

	// If normal direction, use it!
	if ( ubTacticalDirection != 255 )
	{
		gsAdjacentSectorX				= (INT16)(gWorldSectorX + DirXIncrementer[ ubTacticalDirection ]);
		gsAdjacentSectorY				= (INT16)(gWorldSectorY + DirYIncrementer[ ubTacticalDirection ]);
		gbAdjacentSectorZ				= pValidSoldier->bSectorZ;
	}
	else
	{
		// Take directions from exit grid info!
		if ( !GetExitGrid( sAdditionalData, &ExitGrid ) )
		{
			AssertMsg( 0, String( "Told to use exit grid at %d but one does not exist", sAdditionalData ) );
		}

		gsAdjacentSectorX				= ExitGrid.ubGotoSectorX;
		gsAdjacentSectorY				= ExitGrid.ubGotoSectorY;
		gbAdjacentSectorZ				= ExitGrid.ubGotoSectorZ;
		gusDestExitGridNo				= ExitGrid.usGridNo;
	}

	// Give guy(s) orders to walk off sector...
	if( pGroup->fPlayer )
	{	//For player groups, update the soldier information
		PLAYERGROUP *curr;
		INT32 sGridNo;
		UINT8				ubNum = 0;

		curr = pGroup->pPlayerList;
		while( curr )
		{
			if ( OK_CONTROLLABLE_MERC( curr->pSoldier) )
			{
				if ( ubTacticalDirection != 255 )
				{
					sGridNo = PickGridNoNearestEdge( curr->pSoldier, ubTacticalDirection );

					curr->pSoldier->sPreTraversalGridNo = curr->pSoldier->sGridNo;

					if (!TileIsOutOfBounds(sGridNo))
					{
						// Save wait code - this will make buddy walk off screen into oblivion
						curr->pSoldier->ubWaitActionToDo = 2;
						// This will set the direction so we know now to move into oblivion
						curr->pSoldier->aiData.uiPendingActionData1		 = ubTacticalDirection;
					}
					else
					{
						AssertMsg( 0, String( "Failed to get good exit location for adjacentmove" ) );
					}

					curr->pSoldier->EVENT_GetNewSoldierPath( sGridNo, WALKING );

				}
				else
				{
						// Here, get closest location for exit grid....
						sGridNo = FindGridNoFromSweetSpotCloseToExitGrid( curr->pSoldier, sAdditionalData, 10, &ubDirection );
												
						if (!TileIsOutOfBounds(sGridNo))
						{
							// Save wait code - this will make buddy walk off screen into oblivion
						//	curr->pSoldier->ubWaitActionToDo = 2;
						}
						else
						{
							AssertMsg( 0, String( "Failed to get good exit location for adjacentmove" ) );
						}

						// Don't worry about walk off screen, just stay at gridno...
						curr->pSoldier->ubWaitActionToDo = 1;

						// Set buddy go!
						gfPlotPathToExitGrid = TRUE;
						curr->pSoldier->EVENT_GetNewSoldierPath( sGridNo, WALKING );
						gfPlotPathToExitGrid = FALSE;

				}
				ubNum++;
			}
			else
			{
				// We will remove them later....
			}
			curr = curr->next;
		}

		// ATE: Do another round, removing guys from group that can't go on...
BEGINNING_LOOP:

		curr = pGroup->pPlayerList;
		while( curr )
		{
			if ( !OK_CONTROLLABLE_MERC( curr->pSoldier ) )
			{
				if(OK_CONTROL_MERC(curr->pSoldier) && curr->pSoldier->bAssignment == VEHICLE && pGroup->fVehicle)
				{
					//CHRISL: passengers in a vehicle movement group will not pass the OK_CONTROLLABLE_MERC check because their assignment is not "ON_DUTY".
					//	The above conditions should allow passengers in a vehicle movement group to remain in the group.
				}
				else
				{
					RemoveCharacterFromSquads( curr->pSoldier );
					goto BEGINNING_LOOP;
				}
			}
			curr = curr->next;
		}


		// OK, setup TacticalOverhead polling system that will notify us once everybody
		// has made it to our destination.
		if ( ubTacticalDirection != 255 )
		{
			SetActionToDoOnceMercsGetToLocation( WAIT_FOR_MERCS_TO_WALKOFF_SCREEN, ubNum, ubJumpCode, 0, 0 );
		}
		else
		{
			// Add new wait action here...
			SetActionToDoOnceMercsGetToLocation( WAIT_FOR_MERCS_TO_WALK_TO_GRIDNO, ubNum, ubJumpCode, 0, 0 );
		}

		// Lock UI!
		guiPendingOverrideEvent = LU_BEGINUILOCK;
		HandleTacticalUI( );
	}
}


void HandleSoldierLeavingSectorByThemSelf( SOLDIERTYPE *pSoldier )
{
	// soldier leaving thier squad behind, will rejoin later
	// if soldier in a squad, set the fact they want to return here
	UINT8 ubGroupId;

	if( pSoldier->bAssignment < ON_DUTY )
	{
			RemoveCharacterFromSquads( pSoldier );

		// are they in a group?..remove from group
		if( pSoldier->ubGroupID != 0 )
		{
			// remove from group
			RemovePlayerFromGroup(  pSoldier->ubGroupID, pSoldier );
			pSoldier->ubGroupID = 0;
		}
	}
	else
	{
		// otherwise, they are on thier own, not in a squad, simply remove mvt group
		if( pSoldier->ubGroupID && pSoldier->bAssignment != VEHICLE )
		{ //Can only remove groups if they aren't persistant (not in a squad or vehicle)
			// delete group
			RemoveGroup( pSoldier->ubGroupID );
			pSoldier->ubGroupID = 0;
		}
	}

	// set to guard
	AddCharacterToUniqueSquad( pSoldier );

	if( pSoldier->ubGroupID == 0 )
	{
	  // create independant group
	  ubGroupId = CreateNewPlayerGroupDepartingFromSector( ( UINT8 )pSoldier->sSectorX, ( UINT8 )pSoldier->sSectorY );
	  AddPlayerToGroup( ubGroupId , pSoldier );
	}

	return;
}

void AllMercsWalkedToExitGrid()
{
	PLAYERGROUP *pPlayer;
	BOOLEAN fDone;
	// SANDRO was here.. made some tweaks, fixed some stuff
	BOOLEAN fEnemiesInLoadedSector = FALSE;
	if( NumEnemiesInAnySector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) )
	{
		fEnemiesInLoadedSector = TRUE;
		////////////////////////////////////////////////////////////////////////////////////////
		// SANDRO - if enemies are in sector, handle morale hit for fleeing
		// Minor bug fix - check certain circumstances and don't reduce the morale if the fight is not over yet
		// if all mercs leaving, morale gets hit
		// if one merc only leaves, but there are others fighting on, don't reduce the morale
		// if sector is about to be loaded and there are enemies in the current, then all mercs are leaving, so the morale gets hit
		if( ( gubAdjacentJumpCode == JUMP_ALL_NO_LOAD && gpAdjacentGroup->ubGroupSize >= PlayerMercsInSector( (UINT8)gWorldSectorX, (UINT8)gWorldSectorY, (UINT8)gbWorldSectorZ )) ||
			( gubAdjacentJumpCode == JUMP_SINGLE_NO_LOAD && PlayerMercsInSector( (UINT8)gWorldSectorX, (UINT8)gWorldSectorY, (UINT8)gbWorldSectorZ ) <= 1 ) ||
			( gubAdjacentJumpCode == JUMP_ALL_LOAD_NEW || gubAdjacentJumpCode == JUMP_SINGLE_LOAD_NEW ) ) 
		{
			HandleLoyaltyImplicationsOfMercRetreat( RETREAT_TACTICAL_TRAVERSAL, gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
		
			// End inetrrogation quest if we left the sector, but haven't killed all enemies
			if ( gWorldSectorX == 7 && gWorldSectorY == 14 && gbWorldSectorZ == 0 && gubQuest[ QUEST_INTERROGATION ] == QUESTINPROGRESS )
			{
				// Finish quest, although not give points here...
				InternalEndQuest( QUEST_INTERROGATION, gWorldSectorX, gWorldSectorY, FALSE );
				// ... give them manually, but halved
				GiveQuestRewardPoint( gWorldSectorX, gWorldSectorY, 4, NO_PROFILE );
				// Also get us know, we finished the quest
				ResetHistoryFact( QUEST_INTERROGATION, gWorldSectorX, gWorldSectorY );
			}
		}
		////////////////////////////////////////////////////////////////////////////////////////

		HandlePotentialMoraleHitForSkimmingSectors( gpAdjacentGroup );
	}

	if( gubAdjacentJumpCode == JUMP_ALL_NO_LOAD || gubAdjacentJumpCode == JUMP_SINGLE_NO_LOAD )
	{
		Assert( gpAdjacentGroup );
		//pPlayer = gpAdjacentGroup->pPlayerList; // SANDRO - why was this here twice?
		pPlayer = gpAdjacentGroup->pPlayerList;
		while( pPlayer )
		{
			/////////////////////////////////////////////////////////////////////////////////
			// SANDRO - merc records - times retreated counter
			if( fEnemiesInLoadedSector && pPlayer->pSoldier->ubProfile != NO_PROFILE )
				gMercProfiles[ pPlayer->pSoldier->ubProfile ].records.usBattlesRetreated++;
			/////////////////////////////////////////////////////////////////////////////////

			SetInsertionDataFromAdjacentMoveDirection( pPlayer->pSoldier, gubTacticalDirection, gsAdditionalData );

			RemoveSoldierFromTacticalSector( pPlayer->pSoldier, TRUE );

			pPlayer = pPlayer->next;
		}

		SetGroupSectorValue( gsAdjacentSectorX, gsAdjacentSectorY, (INT16) gbAdjacentSectorZ, gpAdjacentGroup->ubGroupID );
		AttemptToMergeSeparatedGroups( gpAdjacentGroup, FALSE );

		SetDefaultSquadOnSectorEntry( TRUE );

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////
		// SANDRO - reset num enemies here if the sector was unloaded
		if( gWorldSectorX == 0 && gWorldSectorX == 0 && gbWorldSectorZ == -1 )
		{
			memset( &(gTacticalStatus.bNumFoughtInBattle), 0, MAXTEAMS );
		}
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////
	}
	else
	{
		// SANDRO - reset number of enemies here, we are about to load a new sector
		memset( &(gTacticalStatus.bNumFoughtInBattle), 0, MAXTEAMS ); 

		//Because we are actually loading the new map, and we are physically traversing, we don't want
		//to bring up the prebattle interface when we arrive if there are enemies there.  This flag
		//ignores the initialization of the prebattle interface and clears the flag.
		gfTacticalTraversal = TRUE;
		gpTacticalTraversalGroup = gpAdjacentGroup;

		//Check for any unconcious and/or dead merc and remove them from the current squad, so that they
		//don't get moved to the new sector.
		fDone = FALSE;
		while( !fDone )
		{
			fDone = FALSE;
			pPlayer = gpAdjacentGroup->pPlayerList;
			while( pPlayer )
			{
				if( pPlayer->pSoldier->stats.bLife < OKLIFE )
				{
					AddCharacterToUniqueSquad( pPlayer->pSoldier );
					break;
				}
				InitSoldierOppList( pPlayer->pSoldier );
				pPlayer = pPlayer->next;
			}
			if( !pPlayer )
			{
				fDone = TRUE;
			}
		}

		// OK, Set insertion direction for all these guys....
		Assert( gpAdjacentGroup );
		pPlayer = gpAdjacentGroup->pPlayerList;
		while( pPlayer )
		{
			/////////////////////////////////////////////////////////////////////////////////
			// SANDRO - merc records - times retreated counter
			if( fEnemiesInLoadedSector && pPlayer->pSoldier->ubProfile != NO_PROFILE )
				gMercProfiles[ pPlayer->pSoldier->ubProfile ].records.usBattlesRetreated++;
			/////////////////////////////////////////////////////////////////////////////////

			SetInsertionDataFromAdjacentMoveDirection( pPlayer->pSoldier, gubTacticalDirection, gsAdditionalData );

			pPlayer = pPlayer->next;
		}
		SetGroupSectorValue( gsAdjacentSectorX, gsAdjacentSectorY, (INT16) gbAdjacentSectorZ, gpAdjacentGroup->ubGroupID );
		AttemptToMergeSeparatedGroups( gpAdjacentGroup, FALSE );

		gFadeOutDoneCallback = DoneFadeOutExitGridSector;
		FadeOutGameScreen( );
	}
	// SANDRO - This actually never happeneds, moved up
	//if( !PlayerMercsInSector( (UINT8)gsAdjacentSectorX, (UINT8)gsAdjacentSectorY, (UINT8)gbAdjacentSectorZ ) )
	//{
	//	HandleLoyaltyImplicationsOfMercRetreat( RETREAT_TACTICAL_TRAVERSAL, gsAdjacentSectorX, gsAdjacentSectorY, gbAdjacentSectorZ );
	//}
	if( gubAdjacentJumpCode == JUMP_ALL_NO_LOAD || gubAdjacentJumpCode == JUMP_SINGLE_NO_LOAD )
	{
		gfTacticalTraversal = FALSE;
		gpTacticalTraversalGroup = NULL;
		gpTacticalTraversalChosenSoldier = NULL;
	}
}

void SetupTacticalTraversalInformation()
{
	SOLDIERTYPE *pSoldier;
	PLAYERGROUP *pPlayer;

	Assert( gpAdjacentGroup );
	pPlayer = gpAdjacentGroup->pPlayerList;
	while( pPlayer )
	{
		pSoldier = pPlayer->pSoldier;

		SetInsertionDataFromAdjacentMoveDirection( pSoldier, gubTacticalDirection, gsAdditionalData );

		// pass flag that this is a tactical traversal, the path built MUST go in the traversed direction even if longer!
		PlotPathForCharacter( pSoldier, gsAdjacentSectorX, gsAdjacentSectorY, TRUE );

		//dnl ch48 041009
		if(guiAdjacentTraverseTime <= 5)
		{
			// Save this gridNo....
			pSoldier->aiData.sPendingActionData2 = GETWORLDINDEXFROMWORLDCOORDS(pSoldier->sY, pSoldier->sX);
			// Copy CODe computed earlier into data
			pSoldier->usStrategicInsertionData = pSoldier->ubStrategicInsertionCode;
			// Now use NEW code....
			pSoldier->ubStrategicInsertionCode = CalcMapEdgepointClassInsertionCode(pSoldier->sPreTraversalGridNo);
			if(gubAdjacentJumpCode == JUMP_SINGLE_LOAD_NEW || gubAdjacentJumpCode == JUMP_ALL_LOAD_NEW)
				fUsingEdgePointsForStrategicEntry = TRUE;
		}

		pPlayer = pPlayer->next;
	}
	if( gubAdjacentJumpCode == JUMP_ALL_NO_LOAD || gubAdjacentJumpCode == JUMP_SINGLE_NO_LOAD )
	{
		gfTacticalTraversal = FALSE;
		gpTacticalTraversalGroup = NULL;
		gpTacticalTraversalChosenSoldier = NULL;
	}
}

void AllMercsHaveWalkedOffSector( )
{
	DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"AllMercsHaveWalkedOffSector");

	PLAYERGROUP *pPlayer;
	BOOLEAN fEnemiesInLoadedSector = FALSE;

	if( NumEnemiesInAnySector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) )
	{
		fEnemiesInLoadedSector = TRUE;
		////////////////////////////////////////////////////////////////////////////////////////
		// SANDRO - if enemies are in sector, handle morale hit for fleeing
		// Minor bug fix - check certain circumstances and don't reduce the morale if the fight is not over yet
		// if all mercs leaving, morale gets hit
		// if one merc only leaves, but there are others fighting on, don't reduce the morale
		// if sector is about to be loaded and there are enemies in the current, then all mercs are leaving alway
		if( ( gubAdjacentJumpCode == JUMP_ALL_NO_LOAD && gpAdjacentGroup->ubGroupSize >= PlayerMercsInSector( (UINT8)gWorldSectorX, (UINT8)gWorldSectorY, (UINT8)gbWorldSectorZ )) ||
			( gubAdjacentJumpCode == JUMP_SINGLE_NO_LOAD && PlayerMercsInSector( (UINT8)gWorldSectorX, (UINT8)gWorldSectorY, (UINT8)gbWorldSectorZ ) <= 1 ) ||
			( gubAdjacentJumpCode == JUMP_ALL_LOAD_NEW || gubAdjacentJumpCode == JUMP_SINGLE_LOAD_NEW ) ) 
		{
			HandleLoyaltyImplicationsOfMercRetreat( RETREAT_TACTICAL_TRAVERSAL, gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
		
			// End inetrrogation quest if we left the sector, but haven't killed all enemies
			if ( gWorldSectorX == 7 && gWorldSectorY == 14 && gbWorldSectorZ == 0 && gubQuest[ QUEST_INTERROGATION ] == QUESTINPROGRESS )
			{
				// Finish quest, although not give points here...
				InternalEndQuest( QUEST_INTERROGATION, gWorldSectorX, gWorldSectorY, FALSE );
				// ... give them manually, but halved
				GiveQuestRewardPoint( gWorldSectorX, gWorldSectorY, 4, NO_PROFILE );
				// Also get us know, we finished the quest
				ResetHistoryFact( QUEST_INTERROGATION, gWorldSectorX, gWorldSectorY );
			}
		}
		////////////////////////////////////////////////////////////////////////////////////////
	}

	//Setup strategic traversal information
	if( guiAdjacentTraverseTime <= 5 )
	{
		gfTacticalTraversal = TRUE;
		gpTacticalTraversalGroup = gpAdjacentGroup;

		if( gbAdjacentSectorZ > 0 && guiAdjacentTraverseTime <= 5 )
		{	//Nasty strategic movement logic doesn't like underground sectors!
			gfUndergroundTacticalTraversal = TRUE;
		}
	}
	ClearMercPathsAndWaypointsForAllInGroup( gpAdjacentGroup );
	AddWaypointToPGroup( gpAdjacentGroup, (UINT8)gsAdjacentSectorX, (UINT8)gsAdjacentSectorY );
	if( gbAdjacentSectorZ > 0 && guiAdjacentTraverseTime <= 5 )
	{	//Nasty strategic movement logic doesn't like underground sectors!
		gfUndergroundTacticalTraversal = TRUE;
	}

	SetupTacticalTraversalInformation();

	// ATE: Added here: donot load another screen if we were told not to....
	if( ( gubAdjacentJumpCode == JUMP_ALL_NO_LOAD || gubAdjacentJumpCode == JUMP_SINGLE_NO_LOAD ) )
	{ //Case 1:  Group is leaving sector, but there are other mercs in sector and player wants to stay, or
		//         there are other mercs in sector while a battle is in progress.
		pPlayer = gpAdjacentGroup->pPlayerList;
		while( pPlayer )
		{
			/////////////////////////////////////////////////////////////////////////////////
			// SANDRO - merc records - times retreated counter
			if( fEnemiesInLoadedSector && pPlayer->pSoldier->ubProfile != NO_PROFILE )
				gMercProfiles[ pPlayer->pSoldier->ubProfile ].records.usBattlesRetreated++;
			/////////////////////////////////////////////////////////////////////////////////
			RemoveSoldierFromTacticalSector( pPlayer->pSoldier, TRUE );
			pPlayer = pPlayer->next;
		}
		SetDefaultSquadOnSectorEntry( TRUE );
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////
		// SANDRO - reset num enemies here if the sector was unloaded
		if( !fEnemiesInLoadedSector || (gWorldSectorX == 0 && gWorldSectorX == 0 && gbWorldSectorZ == -1 ) )
		{
			memset( &(gTacticalStatus.bNumFoughtInBattle), 0, MAXTEAMS );
		}
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////
	}
	else
	{
		// SANDRO - reset number of enemies here, we are about to load a new sector
		memset( &(gTacticalStatus.bNumFoughtInBattle), 0, MAXTEAMS ); 

		if( fEnemiesInLoadedSector )
		{
			/////////////////////////////////////////////////////////////////////////////////
			// SANDRO - merc records - times retreated counter
			pPlayer = gpAdjacentGroup->pPlayerList;
			while( pPlayer )
			{
				if( fEnemiesInLoadedSector && pPlayer->pSoldier->ubProfile != NO_PROFILE )
					gMercProfiles[ pPlayer->pSoldier->ubProfile ].records.usBattlesRetreated++;
				pPlayer = pPlayer->next;
			}
			/////////////////////////////////////////////////////////////////////////////////
			//We are retreating from a sector with enemies in it and there are no mercs left  so
			//warp the game time by 5 minutes to simulate the actual retreat.  This restricts the
			//player from immediately coming back to the same sector they left to perhaps take advantage
			//of the tactical placement gui to get into better position.  Additionally, if there are any
			//enemies in this sector that are part of a movement group, reset that movement group so that they
			//are "in" the sector rather than 75% of the way to the next sector if that is the case.
			ResetMovementForEnemyGroupsInLocation( (UINT8)gWorldSectorX, (UINT8)gWorldSectorY );

			if( guiAdjacentTraverseTime > 5 )
			{
				//Because this final group is retreating, simulate extra time to retreat, so they can't immediately come back.
				WarpGameTime( 300, WARPTIME_NO_PROCESSING_OF_EVENTS );
			}
		}
		if( guiAdjacentTraverseTime <= 5 )
		{
			//Case 2:  Immediatly loading the next sector
			if( !gbAdjacentSectorZ )
			{
				UINT32 uiWarpTime;
				uiWarpTime = (GetWorldTotalMin() + 5) * 60 - GetWorldTotalSeconds();
				WarpGameTime( uiWarpTime, WARPTIME_PROCESS_TARGET_TIME_FIRST );
			}
			else if( gbAdjacentSectorZ > 0 )
			{
				UINT32 uiWarpTime;
				uiWarpTime = (GetWorldTotalMin() + 1) * 60 - GetWorldTotalSeconds();
				WarpGameTime( uiWarpTime, WARPTIME_PROCESS_TARGET_TIME_FIRST );
			}

			//Because we are actually loading the new map, and we are physically traversing, we don't want
			//to bring up the prebattle interface when we arrive if there are enemies there.  This flag
			//ignores the initialization of the prebattle interface and clears the flag.
			gFadeOutDoneCallback = DoneFadeOutAdjacentSector;
			FadeOutGameScreen( );
		}
		else
		{ //Case 3:  Going directly to mapscreen

			//Lock game into mapscreen mode, but after the fade is done.
			gfEnteringMapScreen = TRUE;

			// ATE; Fade FAST....
			SetMusicFadeSpeed( 5 );
			SetMusicMode( MUSIC_TACTICAL_NOTHING );
		}
	}
	DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"AllMercsHaveWalkedOffSector done");
}

void DoneFadeOutExitGridSector( )
{
	SetCurrentWorldSector( gsAdjacentSectorX, gsAdjacentSectorY, gbAdjacentSectorZ );
	if( gfTacticalTraversal && gpTacticalTraversalGroup && gpTacticalTraversalChosenSoldier )
	{
		if( gTacticalStatus.fEnemyInSector )
		{
			TacticalCharacterDialogueWithSpecialEvent( gpTacticalTraversalChosenSoldier, QUOTE_ENEMY_PRESENCE, 0, 0, 0 );
		}
	}
	gfTacticalTraversal = FALSE;
	gpTacticalTraversalGroup = NULL;
	gpTacticalTraversalChosenSoldier = NULL;
	FadeInGameScreen( );
}

void DoneFadeOutAdjacentSector( )
{
	UINT8 ubDirection;
	SetCurrentWorldSector( gsAdjacentSectorX, gsAdjacentSectorY, gbAdjacentSectorZ );

	ubDirection = GetStrategicInsertionDataFromAdjacentMoveDirection( gubTacticalDirection, gsAdditionalData );
	if( gfTacticalTraversal && gpTacticalTraversalGroup && gpTacticalTraversalChosenSoldier )
	{
		if( gTacticalStatus.fEnemyInSector )
		{
			TacticalCharacterDialogueWithSpecialEvent( gpTacticalTraversalChosenSoldier, QUOTE_ENEMY_PRESENCE, 0, 0, 0 );
		}
	}
	gfTacticalTraversal = FALSE;
	gpTacticalTraversalGroup = NULL;
	gpTacticalTraversalChosenSoldier = NULL;

	if ( gfCaves )
	{
		// ATE; Set tactical status flag...
		gTacticalStatus.uiFlags |= IGNORE_ALL_OBSTACLES;
		// Set pathing flag to path through anything....
		gfPathAroundObstacles = FALSE;
	}

	// OK, give our guys new orders...
	if( gpAdjacentGroup->fPlayer )
	{
		//For player groups, update the soldier information
		PLAYERGROUP *curr;
		UINT32 uiAttempts;
		INT32 sGridNo, sOldGridNo;
		UINT8				ubNum = 0;
		INT16 sWorldX, sWorldY;
		curr = gpAdjacentGroup->pPlayerList;
		while( curr )
		{
			if( !(curr->pSoldier->flags.uiStatusFlags & SOLDIER_IS_TACTICALLY_VALID) )
			{				
				if(!TileIsOutOfBounds(curr->pSoldier->sGridNo))
				{
					sGridNo = PickGridNoToWalkIn( curr->pSoldier, ubDirection, &uiAttempts );

					//If the search algorithm failed due to too many attempts, simply reset the
					//the gridno as the destination is a reserved gridno and we will place the
					//merc there without walking into the sector.
					
					if (TileIsOutOfBounds(sGridNo) && uiAttempts == MAX_ATTEMPTS )					
					{
						sGridNo = curr->pSoldier->sGridNo;
					}

					if(!TileIsOutOfBounds(sGridNo))					
					{
						curr->pSoldier->ubWaitActionToDo = 1;
						// OK, here we have been given a position, a gridno has been given to use as well....
						sOldGridNo = curr->pSoldier->sGridNo;
						sWorldX = CenterX( sGridNo );
						sWorldY = CenterY( sGridNo );
						curr->pSoldier->EVENT_SetSoldierPosition( sWorldX, sWorldY );
						if( sGridNo != sOldGridNo )
						{
							curr->pSoldier->EVENT_GetNewSoldierPath( sOldGridNo, WALKING );
						}
						ubNum++;
					}
				}
				else
				{
					#ifdef JA2BETAVERSION
						CHAR8 str[256];
						sprintf( str, "%S's gridno is NOWHERE, and is attempting to walk into sector.", curr->pSoldier->name );
						DebugMsg( TOPIC_JA2, DBG_LEVEL_3, str );
					#endif
				}
			}
			curr = curr->next;
		}
		SetActionToDoOnceMercsGetToLocation( WAIT_FOR_MERCS_TO_WALKON_SCREEN, ubNum, 0, 0, 0 );
		guiPendingOverrideEvent = LU_BEGINUILOCK;
		HandleTacticalUI( );

		// Unset flag here.....
		gfPathAroundObstacles = TRUE;

	}
	FadeInGameScreen( );
}



BOOLEAN SoldierOKForSectorExit( SOLDIERTYPE * pSoldier, INT8 bExitDirection, INT32 usAdditionalData )//dnl ch56 151009
{
	INT16 sXMapPos;
	INT16 sYMapPos;
	INT16 sWorldX;
	INT16 sWorldY;
	UINT8	ubDirection;
	INT32 sGridNo;
	INT16	sAPs;

	// if the soldiers gridno is not NOWHERE	
	if(TileIsOutOfBounds(pSoldier->sGridNo))
		return( FALSE );

	// OK, anyone on roofs cannot!
	if ( pSoldier->pathing.bLevel > 0 )
		return( FALSE );

	// get world absolute XY
	ConvertGridNoToXY( pSoldier->sGridNo, &sXMapPos, &sYMapPos );

	// Get screen coordinates for current position of soldier
	GetWorldXYAbsoluteScreenXY( sXMapPos, sYMapPos, &sWorldX, &sWorldY);

	// Check direction
	switch( bExitDirection )
	{
		case EAST_STRATEGIC_MOVE:

			if ( sWorldX < ( ( gsTRX - gsTLX ) - CHECK_DIR_X_DELTA ) )
			{
				// NOT OK, return FALSE
				return( FALSE );
			}
			break;

		case WEST_STRATEGIC_MOVE:

			if ( sWorldX > CHECK_DIR_X_DELTA )
			{
				// NOT OK, return FALSE
				return( FALSE );
			}
			break;

		case SOUTH_STRATEGIC_MOVE:

			if ( sWorldY < ( ( gsBLY - gsTRY ) - CHECK_DIR_Y_DELTA ) )
			{
				// NOT OK, return FALSE
				return( FALSE );
			}
			break;

		case NORTH_STRATEGIC_MOVE:

			if ( sWorldY > CHECK_DIR_Y_DELTA )
			{
				// NOT OK, return FALSE
				return( FALSE );
			}
			break;

			// This case is for an exit grid....
			// check if we are close enough.....

		case -1:


			// FOR REALTIME - DO MOVEMENT BASED ON STANCE!
			if ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) )
			{
				pSoldier->usUIMovementMode =  pSoldier->GetMoveStateBasedOnStance( gAnimControl[ pSoldier->usAnimState ].ubEndHeight );
			}

			sGridNo = FindGridNoFromSweetSpotCloseToExitGrid( pSoldier, usAdditionalData, 10, &ubDirection );
			
			if (TileIsOutOfBounds(sGridNo))
			{
				return( FALSE );
			}

			// ATE: if we are in combat, get cost to move here....
			if ( gTacticalStatus.uiFlags & INCOMBAT )
			{
				// Turn off at end of function...
				sAPs = PlotPath( pSoldier, sGridNo, NO_COPYROUTE, NO_PLOT, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints );

				if ( !EnoughPoints( pSoldier, sAPs, 0, FALSE ) )
				{
					return( FALSE );
				}
			}
			break;

	}
	return( TRUE );
}

//ATE: Returns FALSE if NOBODY is close enough, 1 if ONLY selected guy is and 2 if all on squad are...
BOOLEAN OKForSectorExit( INT8 bExitDirection, INT32 usAdditionalData, UINT32 *puiTraverseTimeInMinutes )//dnl ch56 151009
{
	INT32 cnt;
	SOLDIERTYPE		*pSoldier;
	BOOLEAN		fAtLeastOneMercControllable = FALSE;
	BOOLEAN		fOnlySelectedGuy = FALSE;
	SOLDIERTYPE *pValidSoldier = NULL;
	UINT8			ubReturnVal = FALSE;
	UINT8			ubNumControllableMercs = 0;
	UINT8		  ubNumMercs = 0, ubNumEPCs = 0;
	UINT8     ubPlayerControllableMercsInSquad = 0;

	if( gusSelectedSoldier == NOBODY )
	{ //must have a selected soldier to be allowed to tactically traverse.
		return FALSE;
	}

	/*
	//Exception code for the two sectors in San Mona that are separated by a cliff.  We want to allow strategic
	//traversal, but NOT tactical traversal.  The only way to tactically go from D4 to D5 (or viceversa) is to enter
	//the cave entrance.
	if( gWorldSectorX == 4 && gWorldSectorY == 4 && !gbWorldSectorZ && bExitDirection == EAST_STRATEGIC_MOVE )
	{
		gfInvalidTraversal = TRUE;
		return FALSE;
	}
	if( gWorldSectorX == 5 && gWorldSectorY == 4 && !gbWorldSectorZ && bExitDirection == WEST_STRATEGIC_MOVE )
	{
		gfInvalidTraversal = TRUE;
		return FALSE;
	}
	*/

	gfInvalidTraversal = FALSE;
	gfLoneEPCAttemptingTraversal = FALSE;
	gubLoneMercAttemptingToAbandonEPCs = 0;
	gbPotentiallyAbandonedEPCSlotID = -1;

	// Look through all mercs and check if they are within range of east end....
	cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;

  // look for all mercs on the same team,
  for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++)
	{
		// If we are controllable
		if ( OK_CONTROLLABLE_MERC( pSoldier) && pSoldier->bAssignment == CurrentSquad( ) )
		{
			//Need to keep a copy of a good soldier, so we can access it later, and
			//not more than once.
			pValidSoldier = pSoldier;

			ubNumControllableMercs++;

			//We need to keep track of the number of EPCs and mercs in this squad.  If we have
			//only one merc and one or more EPCs, then we can't allow the merc to tactically traverse,
			//if he is the only merc near enough to traverse.
			if( AM_AN_EPC( pSoldier ) )
			{
				ubNumEPCs++;
				//Also record the EPC's slot ID incase we later build a string using the EPC's name.
				gbPotentiallyAbandonedEPCSlotID = (INT8)cnt;
				if( AM_A_ROBOT( pSoldier ) && !pSoldier->CanRobotBeControlled( ) )
				{
					gfRobotWithoutControllerAttemptingTraversal = TRUE;
					ubNumControllableMercs--;
					continue;
				}
			}
			else
			{
				ubNumMercs++;
			}

			if ( SoldierOKForSectorExit( pSoldier, bExitDirection, usAdditionalData ) )
			{
				fAtLeastOneMercControllable++;

				if ( cnt == gusSelectedSoldier )
				{
					fOnlySelectedGuy = TRUE;
				}
			}
			else
			{
				GROUP *pGroup;

				// ATE: Dont's assume exit grids here...
				if ( bExitDirection != -1 )
				{
					//Now, determine if this is a valid path.
					pGroup = GetGroup( pValidSoldier->ubGroupID );
					AssertMsg( pGroup, String( "%S is not in a valid group (pSoldier->ubGroupID is %d)",pValidSoldier->name, pValidSoldier->ubGroupID) );
					if( !gbWorldSectorZ )
					{
						*puiTraverseTimeInMinutes = GetSectorMvtTimeForGroup( (UINT8)SECTOR( pGroup->ubSectorX, pGroup->ubSectorY ), bExitDirection, pGroup );
					}
					else if( gbWorldSectorZ > 1 )
					{ //We are attempting to traverse in an underground environment.  We need to use a complete different
						//method.  When underground, all sectors are instantly adjacent.
						*puiTraverseTimeInMinutes = UndergroundTacticalTraversalTime( bExitDirection );
					}
					if( *puiTraverseTimeInMinutes == 0xffffffff )
					{
						gfInvalidTraversal = TRUE;
						return FALSE;
					}
				}
				else
				{
					*puiTraverseTimeInMinutes = 0; //exit grid travel is instantaneous
				}
			}
		}
	}

	// If we are here, at least one guy is controllable in this sector, at least he can go!
	if( fAtLeastOneMercControllable )
	{
		ubPlayerControllableMercsInSquad = (UINT8)NumberOfPlayerControllableMercsInSquad( MercPtrs[ gusSelectedSoldier ]->bAssignment );
		if( fAtLeastOneMercControllable <= ubPlayerControllableMercsInSquad )
		{ //if the selected merc is an EPC and we can only leave with that merc, then prevent it
			//as EPCs aren't allowed to leave by themselves.  Instead of restricting this in the
			//exiting sector gui, we restrict it by explaining it with a message box.
			if ( AM_AN_EPC( MercPtrs[ gusSelectedSoldier ] ) )
			{
				if( AM_A_ROBOT( pSoldier ) && !pSoldier->CanRobotBeControlled( ) )
				{
					//gfRobotWithoutControllerAttemptingTraversal = TRUE;
					return FALSE;
				}
				else if( fAtLeastOneMercControllable < ubPlayerControllableMercsInSquad || fAtLeastOneMercControllable == 1 )
				{
					gfLoneEPCAttemptingTraversal = TRUE;
					return FALSE;
				}
			}
			else
			{	//We previously counted the number of EPCs and mercs, and if the selected merc is not an EPC and there are no
				//other mercs in the squad able to escort the EPCs, we will prohibit this merc from tactically traversing.
				if( ubNumEPCs && ubNumMercs == 1 && fAtLeastOneMercControllable < ubPlayerControllableMercsInSquad )
				{
					gubLoneMercAttemptingToAbandonEPCs = ubNumEPCs;
					return FALSE;
				}
			}
		}
		if ( bExitDirection != -1 )
		{
			GROUP *pGroup;
			//Now, determine if this is a valid path.
			pGroup = GetGroup( pValidSoldier->ubGroupID );
			AssertMsg( pGroup, String( "%S is not in a valid group (pSoldier->ubGroupID is %d)",pValidSoldier->name, pValidSoldier->ubGroupID) );
			if( !gbWorldSectorZ )
			{
				*puiTraverseTimeInMinutes = GetSectorMvtTimeForGroup( (UINT8)SECTOR( pGroup->ubSectorX, pGroup->ubSectorY ), bExitDirection, pGroup );
			}
			else if( gbWorldSectorZ > 0 )
			{ //We are attempting to traverse in an underground environment.  We need to use a complete different
				//method.  When underground, all sectors are instantly adjacent.
				*puiTraverseTimeInMinutes = UndergroundTacticalTraversalTime( bExitDirection );
			}
			if( *puiTraverseTimeInMinutes == 0xffffffff )
			{
				gfInvalidTraversal = TRUE;
				ubReturnVal = FALSE;
			}
			else
			{
				ubReturnVal = TRUE;
			}
		}
		else
		{
			ubReturnVal = TRUE;
			*puiTraverseTimeInMinutes = 0; //exit grid travel is instantaneous
		}
	}

	if ( ubReturnVal )
	{
		// Default to FALSE again, until we see that we have
		ubReturnVal = FALSE;

		if ( fAtLeastOneMercControllable )
		{
			// Do we contain the selected guy?
			if ( fOnlySelectedGuy )
			{
				ubReturnVal = 1;
			}
			// Is the whole squad able to go here?
			if ( fAtLeastOneMercControllable == ubPlayerControllableMercsInSquad )
			{
				ubReturnVal = 2;
			}
		}
	}

	return( ubReturnVal );
}


void SetupNewStrategicGame( )
{
	INT16 sSectorX, sSectorY;

	// Set all sectors as enemy controlled
	for ( sSectorX = 0; sSectorX < MAP_WORLD_X; sSectorX++ )
	{
		for ( sSectorY = 0; sSectorY < MAP_WORLD_Y; sSectorY++ )
		{
			StrategicMap[ CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY ) ].fEnemyControlled = TRUE;
		}
	}
	
#ifdef JA2UB	
	//Ja25
	// Make the initial sector free of enemies
	StrategicMap[ CALCULATE_STRATEGIC_INDEX( JA2_5_START_SECTOR_X, JA2_5_START_SECTOR_Y ) ].fEnemyControlled = FALSE;

#endif

	//Initialize the game time
	InitNewGameClock();
	//Remove all events
	DeleteAllStrategicEvents();

	//Set up all events that get processed daily...
	//.............................................
	BuildDayLightLevels();
	// Check for quests each morning
	AddEveryDayStrategicEvent( EVENT_CHECKFORQUESTS, QUEST_CHECK_EVENT_TIME, 0 );
	// Some things get updated in the very early morning
	AddEveryDayStrategicEvent( EVENT_DAILY_EARLY_MORNING_EVENTS, EARLY_MORNING_TIME, 0 );
	//Daily Update BobbyRay Inventory
	AddEveryDayStrategicEvent( EVENT_DAILY_UPDATE_BOBBY_RAY_INVENTORY, BOBBYRAY_UPDATE_TIME, 0 );
	//Daily Update of the M.E.R.C. site.
	AddEveryDayStrategicEvent( EVENT_DAILY_UPDATE_OF_MERC_SITE, 0, 0 );

#ifdef JA2UB
//Ja25:  No insurance for mercs
//JA25: There is no mines
//Ja25 no town opinions
#else	
	//Daily update of insured mercs
	AddEveryDayStrategicEvent( EVENT_HANDLE_INSURED_MERCS, INSURANCE_UPDATE_TIME,	0 );
	//Daily update of mercs
	AddEveryDayStrategicEvent( EVENT_MERC_DAILY_UPDATE, 0, 0 );
	// Daily mine production processing events
	AddEveryDayStrategicEvent( EVENT_SETUP_MINE_INCOME, 0, 0 );
	// Daily merc reputation processing events
	AddEveryDayStrategicEvent( EVENT_SETUP_TOWN_OPINION, 0, 0 );
	// Daily checks for E-mail from Enrico
	AddEveryDayStrategicEvent( EVENT_ENRICO_MAIL, ENRICO_MAIL_TIME , 0 );
#endif


//	if ( gGameOptions.fAirStrikes )
//	{
//		//Daily check for an air raid
//		AddEveryDayStrategicEvent( EVENT_CHECK_FOR_AIR_RAID, 360, 0 );
//	}
//
	// Hourly update of all sorts of things
	AddPeriodStrategicEvent( EVENT_HOURLY_UPDATE, 60, 0 );
	AddPeriodStrategicEvent( EVENT_QUARTER_HOUR_UPDATE, 15, 0 );

	//Clear any possible battle locator
	gfBlitBattleSectorLocator = FALSE;




	StrategicTurnsNewGame( );
}


// a -1 will be returned upon failure
INT8 GetSAMIdFromSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ )
{
	INT8 bCounter = 0;
	INT16 sSectorValue = 0;

	// check if valid sector
	if( bSectorZ != 0 )
	{
		return( -1 );
	}

	// get the sector value
	sSectorValue = SECTOR( sSectorX, sSectorY );

	// run through list of sam sites
	for( bCounter = 0; bCounter < NUMBER_OF_SAMS; bCounter++ )
	{
		if( pSamList[ bCounter ] == sSectorValue )
		{
			return( bCounter );
		}
	}


	return( -1 );
}


BOOLEAN CanGoToTacticalInSector( INT16 sX, INT16 sY, UINT8 ubZ )
{
	//INT32 cnt;
	//SOLDIERTYPE *pSoldier;

	// if not a valid sector
	if( ( sX < 1 ) || ( sX > 16 ) || ( sY < 1 ) || ( sY > 16 ) || ( ubZ > 3 ) )
	{
		return( FALSE );
	}


	//cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;

	// look for all living, fighting mercs on player's team.  Robot and EPCs qualify!
	//for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++, pSoldier++)
	{
		// ARM: now allows loading of sector with all mercs below OKLIFE as long as they're alive
		//if( ( pSoldier->bActive && pSoldier->stats.bLife ) && !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) &&
		//	( pSoldier->bAssignment != IN_TRANSIT ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) &&
		//	( pSoldier->bAssignment != ASSIGNMENT_DEAD ) && !SoldierAboardAirborneHeli( pSoldier )
		//	)

		//if( (SectorInfo[ SECTOR( gWorldSectorX,gWorldSectorY) ].uiFlags & SF_ALREADY_VISITED) )
		if( GetSectorFlagStatus( sSelMapX, sSelMapY, ( UINT8 )iCurrentMapSectorZ, SF_ALREADY_VISITED ) == TRUE )
		{
			//if ( !pSoldier->flags.fBetweenSectors && pSoldier->sSectorX == sX && pSoldier->sSectorY == sY && pSoldier->bSectorZ == ubZ )
			{
				return( TRUE );
			}
		}
	}

	return( FALSE );
}


INT32 GetNumberOfSAMSitesUnderPlayerControl( void )
{
	INT32 iNumber = 0, iCounter = 0;

	// if the sam site is under player control, up the number
	for( iCounter = 0; iCounter < NUMBER_OF_SAMS; iCounter++ )
	{
		if( StrategicMap[ SECTOR_INFO_TO_STRATEGIC_INDEX( pSamList[ iCounter ] ) ].fEnemyControlled == FALSE )
		{
			iNumber++;
		}
	}

	return( iNumber );
}

INT32 SAMSitesUnderPlayerControl( INT16 sX, INT16 sY )
{
	BOOLEAN fSamSiteUnderControl = FALSE;


	// is this sector a SAM sector?
	if( IsThisSectorASAMSector( sX, sY, 0 ) == TRUE )
	{
		// is it under control by the player
		if( StrategicMap[ CALCULATE_STRATEGIC_INDEX( sX , sY ) ].fEnemyControlled == FALSE )
		{
			// yes
			fSamSiteUnderControl = TRUE;
		}
	}

	return( fSamSiteUnderControl );
}


void UpdateAirspaceControl( void )
{
	INT32 iCounterA = 0, iCounterB = 0;
	UINT8 ubControllingSAM;
	StrategicMapElement *pSAMStrategicMap = NULL;
	BOOLEAN fEnemyControlsAir;


	for( iCounterA = 1; iCounterA < ( INT32 )( MAP_WORLD_X - 1 ); iCounterA++ )
	{
		for( iCounterB = 1; iCounterB < ( INT32 )( MAP_WORLD_Y - 1 ); iCounterB++ )
		{
			// IMPORTANT: B and A are reverse here, since the table is stored transposed
			ubControllingSAM = ubSAMControlledSectors[ iCounterB ][ iCounterA ];

			if ( ( ubControllingSAM >= 1 ) && ( ubControllingSAM <= NUMBER_OF_SAMS ) )
			{
				pSAMStrategicMap = &( StrategicMap[ SECTOR_INFO_TO_STRATEGIC_INDEX( pSamList[ ubControllingSAM - 1 ] ) ] );

				// if the enemies own the controlling SAM site, and it's in working condition
				if( ( pSAMStrategicMap->fEnemyControlled ) && ( pSAMStrategicMap->bSAMCondition >= MIN_CONDITION_FOR_SAM_SITE_TO_WORK ) )
				{
					if (is_networked)
					{
						fEnemyControlsAir = FALSE; // disable SAM airspace restrictions...
					}
					else
					{
						fEnemyControlsAir = TRUE;
					}
				}
				else
				{
					fEnemyControlsAir = FALSE;
				}
			}
			else
			{
				// no controlling SAM site
				fEnemyControlsAir = FALSE;
			}

			StrategicMap[ CALCULATE_STRATEGIC_INDEX( iCounterA, iCounterB ) ].fEnemyAirControlled = fEnemyControlsAir;
		}
	}


	// check if currently selected arrival sector still has secure airspace

	// if it's not enemy air controlled
	if ( StrategicMap[ CALCULATE_STRATEGIC_INDEX( gsMercArriveSectorX, gsMercArriveSectorY ) ].fEnemyAirControlled == TRUE )
	{
		// NOPE!
		CHAR16 sMsgString[ 256 ], sMsgSubString1[ 64 ], sMsgSubString2[ 64 ];

		// get the name of the old sector
		GetSectorIDString( gsMercArriveSectorX, gsMercArriveSectorY, 0, sMsgSubString1, FALSE );

		// move the landing zone over to Omerta
		// HEADROCK HAM 3.5: Externalized coordinates
		if ( StrategicMap[ CALCULATE_STRATEGIC_INDEX( gGameExternalOptions.ubDefaultArrivalSectorX, gGameExternalOptions.ubDefaultArrivalSectorY ) ].fEnemyAirControlled == FALSE )
		{
			gsMercArriveSectorX = gGameExternalOptions.ubDefaultArrivalSectorX;
			gsMercArriveSectorY = gGameExternalOptions.ubDefaultArrivalSectorY;
		}
		else
		{
			for (UINT8 ubSectorX = 1; ubSectorX <= 16; ubSectorX++)
			{
				for (UINT8 ubSectorY = 1; ubSectorY <=16; ubSectorY++)
				{
					if ( StrategicMap[ CALCULATE_STRATEGIC_INDEX( ubSectorX, ubSectorY ) ].fEnemyAirControlled == FALSE )
					{
						gsMercArriveSectorX = ubSectorX;
						gsMercArriveSectorY = ubSectorY;
					}
				}
			}
		}

		// get the name of the new sector
		GetSectorIDString( gsMercArriveSectorX, gsMercArriveSectorY, 0, sMsgSubString2, FALSE );

		// now build the string
		swprintf( sMsgString, pBullseyeStrings[ 4 ], sMsgSubString1, sMsgSubString2 );

		// confirm the change with overlay message
		// HEADROCK HAM 3.5: If we do this during SaveLoad, the message box prevents loading the game at all!
		if (guiCurrentScreen != SAVE_LOAD_SCREEN)
		{
			DoScreenIndependantMessageBox( sMsgString, MSG_BOX_FLAG_OK, NULL );
		}

		// update position of bullseye
		fMapPanelDirty = TRUE;

		// update destination column for any mercs in transit
		fTeamPanelDirty = TRUE;
	}


	// ARM: airspace control now affects refueling site availability, so update that too with every change!
	UpdateRefuelSiteAvailability( );
}


BOOLEAN IsThereAFunctionalSAMSiteInSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ )
{
	if( IsThisSectorASAMSector( sSectorX, sSectorY, bSectorZ ) == FALSE )
	{
		return( FALSE );
	}

	if( StrategicMap[ CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY ) ].bSAMCondition < MIN_CONDITION_FOR_SAM_SITE_TO_WORK )
	{
		return( FALSE );
	}

	return( TRUE );
}

BOOLEAN IsThisSectorASAMSector( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ )
{
	INT32	cnt;

	// is the sector above ground?
	if( bSectorZ != 0 )
	{
		return( FALSE );
	}

	for ( cnt = 0; cnt < NUMBER_OF_SAMS; cnt++ )
	{
		if( ( sSectorX == gpSamSectorX[cnt] ) && ( sSectorY == gpSamSectorY[cnt] ) )
			return( TRUE );
	}

	return ( FALSE );
}


// is this sector part of the town?
BOOLEAN SectorIsPartOfTown( INT8 bTownId, INT16 sSectorX, INT16 sSectorY )
{
	if( StrategicMap[CALCULATE_STRATEGIC_INDEX( sSectorX, sSectorY ) ].bNameId == bTownId )
	{
		// is in the town
		return ( TRUE );
	}

	// not in the town
	return ( FALSE );
}


BOOLEAN SaveStrategicInfoToSavedFile( HWFILE hFile )
{
	UINT32		uiNumBytesWritten=0;
	UINT32		uiSize = sizeof( StrategicMapElement ) * ( MAP_WORLD_X * MAP_WORLD_Y );


	// Save the strategic map information
	FileWrite( hFile, StrategicMap, uiSize, &uiNumBytesWritten );
	if( uiNumBytesWritten != uiSize)
	{
		return(FALSE);
	}

	// Save the Sector Info
	for (int sectorID = 0; sectorID <= 255; ++ sectorID) {
		FileWrite( hFile, &SectorInfo[sectorID], sizeof( SECTORINFO ), &uiNumBytesWritten );
		if( uiNumBytesWritten != sizeof( SECTORINFO )) {
			return(FALSE);
		}
	}
//	uiSize = sizeof( SECTORINFO ) * 256;
//	FileWrite( hFile, SectorInfo, uiSize, &uiNumBytesWritten );
//	if( uiNumBytesWritten != uiSize)
//	{
//		return(FALSE);
//	}

	// Save the SAM Controlled Sector Information
	uiSize = MAP_WORLD_X * MAP_WORLD_Y;
/*
	FileWrite( hFile, ubSAMControlledSectors, uiSize, &uiNumBytesWritten );
	if( uiNumBytesWritten != uiSize)
	{
		return(FALSE);
	}
*/
	FileSeek( hFile, uiSize, FILE_SEEK_FROM_CURRENT );

	// Save the fFoundOrta
	FileWrite( hFile, &fFoundOrta, sizeof( BOOLEAN ), &uiNumBytesWritten );
	if( uiNumBytesWritten != sizeof( BOOLEAN ) )
	{
		return(FALSE);
	}



	return( TRUE );
}



BOOLEAN LoadStrategicInfoFromSavedFile( HWFILE hFile )
{
	UINT32		uiNumBytesRead=0;
	UINT32		uiSize = sizeof( StrategicMapElement ) * ( MAP_WORLD_X * MAP_WORLD_Y );


	// Load the strategic map information
	FileRead( hFile, StrategicMap, uiSize, &uiNumBytesRead );
	if( uiNumBytesRead != uiSize)
	{
		return(FALSE);
	}

	// Load the Sector Info
	for (int sectorID = 0; sectorID <= 255; ++ sectorID) {
		FileRead( hFile, &SectorInfo[sectorID], sizeof( SECTORINFO ), &uiNumBytesRead );
		if( uiNumBytesRead != sizeof( SECTORINFO )) {
			return(FALSE);
		}
	}
//	uiSize = sizeof( SECTORINFO ) * 256;
//	FileRead( hFile, SectorInfo, uiSize, &uiNumBytesRead );
//	if( uiNumBytesRead != uiSize)
//	{
//		return(FALSE);
//	}

	// Load the SAM Controlled Sector Information
	uiSize = MAP_WORLD_X * MAP_WORLD_Y;
/*
	FileRead( hFile, ubSAMControlledSectors, uiSize, &uiNumBytesRead );
	if( uiNumBytesRead != uiSize)
	{
		return(FALSE);
	}
*/
	FileSeek( hFile, uiSize, FILE_SEEK_FROM_CURRENT );

	// Load the fFoundOrta
	FileRead( hFile, &fFoundOrta, sizeof( BOOLEAN ), &uiNumBytesRead );
	if( uiNumBytesRead != sizeof( BOOLEAN ) )
	{
		return(FALSE);
	}


	return( TRUE );
}


INT32 PickGridNoNearestEdge( SOLDIERTYPE *pSoldier, UINT8 ubTacticalDirection )
{
	INT32 sGridNo, sStartGridNo, sOldGridNo;
	INT8			bOdd = 1, bOdd2 = 1;
	UINT8			bAdjustedDist = 0;
	UINT32		cnt;

	switch( ubTacticalDirection )
	{

		case EAST:

			sGridNo				 = pSoldier->sGridNo;
			sStartGridNo	 = pSoldier->sGridNo;
			sOldGridNo     = pSoldier->sGridNo;

			// Move directly to the right!
			while( GridNoOnVisibleWorldTile( sGridNo ) )
			{
				sOldGridNo = sGridNo;

				if ( bOdd )
				{
					sGridNo -= WORLD_COLS;
				}
				else
				{
					sGridNo++;
				}

				bOdd = (INT8)!bOdd;
			}

			sGridNo			 = sOldGridNo;
			sStartGridNo = sOldGridNo;

			do
			{
				// OK, here we go back one, check for OK destination...
				if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->pathing.bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) )
				{
					return( sGridNo );
				}

				// If here, try another place!
				// ( alternate up/down )
				if ( bOdd2 )
				{
					bAdjustedDist++;

					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo - WORLD_COLS - 1);
					}
				}
				else
				{
					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo + WORLD_COLS + 1);
					}
				}

				bOdd2 = (INT8)(!bOdd2);

			} while( TRUE );

			break;

		case WEST:

			sGridNo				 = pSoldier->sGridNo;
			sStartGridNo	 = pSoldier->sGridNo;
			sOldGridNo     = pSoldier->sGridNo;

			// Move directly to the left!
			while( GridNoOnVisibleWorldTile( sGridNo ) )
			{
				sOldGridNo = sGridNo;

				if ( bOdd )
				{
					sGridNo += WORLD_COLS;
				}
				else
				{
					sGridNo--;
				}

				bOdd = (INT8)!bOdd;
			}

			sGridNo			 = sOldGridNo;
			sStartGridNo = sOldGridNo;

			do
			{
				// OK, here we go back one, check for OK destination...
				if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->pathing.bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) )
				{
					return( sGridNo );
				}

				// If here, try another place!
				// ( alternate up/down )
				if ( bOdd2 )
				{
					bAdjustedDist++;

					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo - WORLD_COLS - 1);
					}
				}
				else
				{
					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo + WORLD_COLS + 1);
					}
				}

				bOdd2 = (INT8)(!bOdd2);

			} while( TRUE );

			break;

		case NORTH:

			sGridNo				 = pSoldier->sGridNo;
			sStartGridNo	 = pSoldier->sGridNo;
			sOldGridNo     = pSoldier->sGridNo;

			// Move directly to the left!
			while( GridNoOnVisibleWorldTile( sGridNo ) )
			{
				sOldGridNo = sGridNo;

				if ( bOdd )
				{
					sGridNo -= WORLD_COLS;
				}
				else
				{
					sGridNo--;
				}

				bOdd = (INT8)(!bOdd);
			}

			sGridNo			 = sOldGridNo;
			sStartGridNo = sOldGridNo;

			do
			{
				// OK, here we go back one, check for OK destination...
				if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->pathing.bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) )
				{
					return( sGridNo );
				}

				// If here, try another place!
				// ( alternate left/right )
				if ( bOdd2 )
				{
					bAdjustedDist++;

					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo + WORLD_COLS - 1);
					}
				}
				else
				{
					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo - WORLD_COLS + 1);
					}
				}

				bOdd2 = (INT8)(!bOdd2);

			} while( TRUE );

			break;

		case SOUTH:

			sGridNo				 = pSoldier->sGridNo;
			sStartGridNo	 = pSoldier->sGridNo;
			sOldGridNo     = pSoldier->sGridNo;

			// Move directly to the left!
			while( GridNoOnVisibleWorldTile( sGridNo ) )
			{
				sOldGridNo = sGridNo;

				if ( bOdd )
				{
					sGridNo += WORLD_COLS;
				}
				else
				{
					sGridNo++;
				}

				bOdd = (INT8)(!bOdd);
			}

			sGridNo			 = sOldGridNo;
			sStartGridNo = sOldGridNo;

			do
			{
				// OK, here we go back one, check for OK destination...
				if ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->pathing.bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) )
				{
					return( sGridNo );
				}

				// If here, try another place!
				// ( alternate left/right )
				if ( bOdd2 )
				{
					bAdjustedDist++;

					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo + WORLD_COLS - 1);
					}
				}
				else
				{
					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo - WORLD_COLS + 1);
					}
				}

				bOdd2 = (INT8)(!bOdd2);

			} while( TRUE );

			break;

	}

	return( NOWHERE );
}


void AdjustSoldierPathToGoOffEdge( SOLDIERTYPE *pSoldier, INT32 sEndGridNo, UINT8 ubTacticalDirection )
{
	INT32 sNewGridNo, sTempGridNo;
	INT32	iLoop;

	// will this path segment actually take us to our desired destination in the first place?
	if (pSoldier->pathing.usPathDataSize + 2 > MAX_PATH_LIST_SIZE)
	{

		sTempGridNo = pSoldier->sGridNo;

		for (iLoop = 0; iLoop < pSoldier->pathing.usPathDataSize; iLoop++)
		{
			sTempGridNo += DirectionInc( (UINT8) pSoldier->pathing.usPathingData[ iLoop ] );
		}

		if (sTempGridNo == sEndGridNo)
		{
			// we can make it, but there isn't enough path room for the two steps required.
			// truncate our path so there's guaranteed the merc will have to generate another
			// path later on...
			pSoldier->pathing.usPathDataSize -= 4;
			return;
		}
		else
		{
			// can't even make it there with these 30 tiles of path, abort...
			return;
		}
	}

	switch( ubTacticalDirection )
	{
		case EAST:

			sNewGridNo = NewGridNo( sEndGridNo, (UINT16)DirectionInc( (UINT8)NORTHEAST ) );

			if ( OutOfBounds( sEndGridNo, sNewGridNo ) )
			{
				return;
			}

			pSoldier->pathing.usPathingData[ pSoldier->pathing.usPathDataSize ] = NORTHEAST;
			pSoldier->pathing.usPathDataSize++;
			pSoldier->pathing.sFinalDestination = sNewGridNo;
			pSoldier->aiData.usActionData = sNewGridNo;

			sTempGridNo = NewGridNo( sNewGridNo, (UINT16)DirectionInc( (UINT8)NORTHEAST ) );

			if ( OutOfBounds( sNewGridNo, sTempGridNo ) )
			{
				return;
			}
			sNewGridNo = sTempGridNo;

			pSoldier->pathing.usPathingData[ pSoldier->pathing.usPathDataSize ] = NORTHEAST;
			pSoldier->pathing.usPathDataSize++;
			pSoldier->pathing.sFinalDestination = sNewGridNo;
			pSoldier->aiData.usActionData = sNewGridNo;

			break;

		case WEST:

			sNewGridNo = NewGridNo( sEndGridNo, (UINT16)DirectionInc( (UINT8)SOUTHWEST ) );

			if ( OutOfBounds( sEndGridNo, sNewGridNo ) )
			{
				return;
			}

			pSoldier->pathing.usPathingData[ pSoldier->pathing.usPathDataSize ] = SOUTHWEST;
			pSoldier->pathing.usPathDataSize++;
			pSoldier->pathing.sFinalDestination = sNewGridNo;
			pSoldier->aiData.usActionData = sNewGridNo;

			sTempGridNo = NewGridNo( sNewGridNo, (UINT16)DirectionInc( (UINT8)SOUTHWEST ) );

			if ( OutOfBounds( sNewGridNo, sTempGridNo ) )
			{
				return;
			}
			sNewGridNo = sTempGridNo;

			pSoldier->pathing.usPathingData[ pSoldier->pathing.usPathDataSize ] = SOUTHWEST;
			pSoldier->pathing.usPathDataSize++;
			pSoldier->pathing.sFinalDestination = sNewGridNo;
			pSoldier->aiData.usActionData = sNewGridNo;
			break;

		case NORTH:

			sNewGridNo = NewGridNo( sEndGridNo, (UINT16)DirectionInc( (UINT8)NORTHWEST ) );

			if ( OutOfBounds( sEndGridNo, sNewGridNo ) )
			{
				return;
			}

			pSoldier->pathing.usPathingData[ pSoldier->pathing.usPathDataSize ] = NORTHWEST;
			pSoldier->pathing.usPathDataSize++;
			pSoldier->pathing.sFinalDestination = sNewGridNo;
			pSoldier->aiData.usActionData = sNewGridNo;

			sTempGridNo = NewGridNo( sNewGridNo, (UINT16)DirectionInc( (UINT8)NORTHWEST ) );

			if ( OutOfBounds( sNewGridNo, sTempGridNo ) )
			{
				return;
			}
			sNewGridNo = sTempGridNo;

			pSoldier->pathing.usPathingData[ pSoldier->pathing.usPathDataSize ] = NORTHWEST;
			pSoldier->pathing.usPathDataSize++;
			pSoldier->pathing.sFinalDestination = sNewGridNo;
			pSoldier->aiData.usActionData = sNewGridNo;

			break;

		case SOUTH:

			sNewGridNo = NewGridNo( sEndGridNo, (UINT16)DirectionInc( (UINT8)SOUTHEAST ) );

			if ( OutOfBounds( sEndGridNo, sNewGridNo ) )
			{
				return;
			}

			pSoldier->pathing.usPathingData[ pSoldier->pathing.usPathDataSize ] = SOUTHEAST;
			pSoldier->pathing.usPathDataSize++;
			pSoldier->pathing.sFinalDestination = sNewGridNo;
			pSoldier->aiData.usActionData = sNewGridNo;

			sTempGridNo = NewGridNo( sNewGridNo, (UINT16)DirectionInc( (UINT8)SOUTHEAST ) );

			if ( OutOfBounds( sNewGridNo, sTempGridNo ) )
			{
				return;
			}
			sNewGridNo = sTempGridNo;

			pSoldier->pathing.usPathingData[ pSoldier->pathing.usPathDataSize ] = SOUTHEAST;
			pSoldier->pathing.usPathDataSize++;
			pSoldier->pathing.sFinalDestination = sNewGridNo;
			pSoldier->aiData.usActionData = sNewGridNo;
			break;

	}
}

INT32 PickGridNoToWalkIn( SOLDIERTYPE *pSoldier, UINT8 ubInsertionDirection, UINT32 *puiNumAttempts )
{
	INT32 sGridNo, sStartGridNo, sOldGridNo;
	INT8			bOdd = 1, bOdd2 = 1;
	UINT8			bAdjustedDist = 0;
	UINT32		cnt;

	*puiNumAttempts = 0;

	switch( ubInsertionDirection )
	{
		// OK, we're given a direction on visible map, let's look for the first oone
		// we find that is just on the start of visible map...
		case INSERTION_CODE_WEST:

			sGridNo				 = pSoldier->sGridNo;
			sStartGridNo	 = pSoldier->sGridNo;
			sOldGridNo     = pSoldier->sGridNo;

			// Move directly to the left!
			while( GridNoOnVisibleWorldTile( sGridNo ) )
			{
				sOldGridNo = sGridNo;

				if ( bOdd )
				{
					sGridNo += WORLD_COLS;
				}
				else
				{
					sGridNo--;
				}

				bOdd = (INT8)(!bOdd);
			}

			sGridNo			 = sOldGridNo;
			sStartGridNo = sOldGridNo;

			while( *puiNumAttempts < MAX_ATTEMPTS )
			{
				(*puiNumAttempts)++;
				// OK, here we go back one, check for OK destination...
				if ( ( gTacticalStatus.uiFlags & IGNORE_ALL_OBSTACLES ) || ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->pathing.bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) ) )
				{
					return( sGridNo );
				}

				// If here, try another place!
				// ( alternate up/down )
				if ( bOdd2 )
				{
					bAdjustedDist++;

					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo - WORLD_COLS - 1);
					}
				}
				else
				{
					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo + WORLD_COLS + 1);
					}
				}

				bOdd2 = (INT8)(!bOdd2);

			}
			return NOWHERE;

		case INSERTION_CODE_EAST:

			sGridNo				 = pSoldier->sGridNo;
			sStartGridNo	 = pSoldier->sGridNo;
			sOldGridNo     = pSoldier->sGridNo;

			// Move directly to the right!
			while( GridNoOnVisibleWorldTile( sGridNo ) )
			{
				sOldGridNo = sGridNo;

				if ( bOdd )
				{
					sGridNo -= WORLD_COLS;
				}
				else
				{
					sGridNo++;
				}

				bOdd = (INT8)(!bOdd);
			}

			sGridNo			 = sOldGridNo;
			sStartGridNo = sOldGridNo;

			while( *puiNumAttempts < MAX_ATTEMPTS )
			{
				(*puiNumAttempts)++;
				// OK, here we go back one, check for OK destination...
				if ( ( gTacticalStatus.uiFlags & IGNORE_ALL_OBSTACLES ) || ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->pathing.bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) ) )
				{
					return( sGridNo );
				}

				// If here, try another place!
				// ( alternate up/down )
				if ( bOdd2 )
				{
					bAdjustedDist++;

					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo - WORLD_COLS - 1);
					}
				}
				else
				{
					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo + WORLD_COLS + 1);
					}
				}

				bOdd2 = (INT8)(!bOdd2);

			}
			return NOWHERE;

		case INSERTION_CODE_NORTH:

			sGridNo				 = pSoldier->sGridNo;
			sStartGridNo	 = pSoldier->sGridNo;
			sOldGridNo     = pSoldier->sGridNo;

			// Move directly to the up!
			while( GridNoOnVisibleWorldTile( sGridNo ) )
			{
				sOldGridNo = sGridNo;

				if ( bOdd )
				{
					sGridNo -= WORLD_COLS;
				}
				else
				{
					sGridNo--;
				}

				bOdd = (INT8)(!bOdd);
			}

			sGridNo			 = sOldGridNo;
			sStartGridNo = sOldGridNo;

			while( *puiNumAttempts < MAX_ATTEMPTS )
			{
				(*puiNumAttempts)++;
				// OK, here we go back one, check for OK destination...
				if ( ( gTacticalStatus.uiFlags & IGNORE_ALL_OBSTACLES ) || ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->pathing.bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) ) )
				{
					return( sGridNo );
				}

				// If here, try another place!
				// ( alternate left/right )
				if ( bOdd2 )
				{
					bAdjustedDist++;

					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo - WORLD_COLS + 1);
					}
				}
				else
				{
					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo + WORLD_COLS - 1);
					}
				}

				bOdd2 = (INT8)(!bOdd2);

			}
			return NOWHERE;

		case INSERTION_CODE_SOUTH:

			sGridNo				 = pSoldier->sGridNo;
			sStartGridNo	 = pSoldier->sGridNo;
			sOldGridNo     = pSoldier->sGridNo;

			// Move directly to the down!
			while( GridNoOnVisibleWorldTile( sGridNo ) )
			{
				sOldGridNo = sGridNo;

				if ( bOdd )
				{
					sGridNo += WORLD_COLS;
				}
				else
				{
					sGridNo++;
				}

				bOdd = (INT8)(!bOdd);
			}

			sGridNo			 = sOldGridNo;
			sStartGridNo = sOldGridNo;

			while( *puiNumAttempts < MAX_ATTEMPTS )
			{
				(*puiNumAttempts)++;
				// OK, here we go back one, check for OK destination...
				if ( ( gTacticalStatus.uiFlags & IGNORE_ALL_OBSTACLES ) || ( NewOKDestination( pSoldier, sGridNo, TRUE, pSoldier->pathing.bLevel ) && FindBestPath( pSoldier, sGridNo, pSoldier->pathing.bLevel, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) ) )
				{
					return( sGridNo );
				}

				// If here, try another place!
				// ( alternate left/right )
				if ( bOdd2 )
				{
					bAdjustedDist++;

					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo - WORLD_COLS + 1);
					}
				}
				else
				{
					sGridNo = sStartGridNo;

					for ( cnt = 0; cnt < bAdjustedDist; cnt++ )
					{
						sGridNo = (sGridNo + WORLD_COLS - 1);
					}
				}

				bOdd2 = (INT8)(!bOdd2);

			}
			return NOWHERE;

	}

	//Unhandled exit
	*puiNumAttempts = 0;

	return( NOWHERE );
}


void GetLoadedSectorString( STR16 pString )
{
	if( !gfWorldLoaded )
	{
		swprintf( pString, L"" );
		return;
	}
	if( gbWorldSectorZ )
	{
		swprintf( pString, L"%c%d_b%d", gWorldSectorY + 'A' - 1, gWorldSectorX, gbWorldSectorZ );
	}
	else if( !gbWorldSectorZ )
	{
		swprintf( pString, L"%c%d", gWorldSectorY + 'A' - 1, gWorldSectorX );
	}
}

#ifdef JA2UB
// no UB
#else
void HandleSlayDailyEvent( void )
{
	SOLDIERTYPE *pSoldier = NULL;

	// grab slay
	pSoldier = FindSoldierByProfileID( 64, TRUE );

	if( pSoldier == NULL )
	{
		return;
	}

	// valid soldier?
	if( ( pSoldier->bActive == FALSE ) || ( pSoldier->stats.bLife == 0 ) || ( pSoldier->bAssignment == IN_TRANSIT ) ||( pSoldier->bAssignment == ASSIGNMENT_POW ) )
	{
		// no
		return;
	}

	// ATE: This function is used to check for the ultimate last day SLAY can stay for
	// he may decide to leave randomly while asleep...
	//if the user hasnt renewed yet, and is still leaving today
	if( ( pSoldier->iEndofContractTime /1440 ) <= (INT32)GetWorldDay( ) )
	{
		pSoldier->ubLeaveHistoryCode = HISTORY_SLAY_MYSTERIOUSLY_LEFT;
		TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_CONTRACT_ENDING_NO_ASK_EQUIP, 0, 0 );
	}
}
#endif

BOOLEAN IsSectorDesert( INT16 sSectorX, INT16 sSectorY )
{
	// HEADROCK HAM 3.5: Fix for assertion errors associated with rain checks.
	if (sSectorX <= 0 || sSectorY <=0 || sSectorX > 16 || sSectorY > 16)
	{
		return( FALSE );
	}

	// SANDRO - added more terrain types for heat intolerant feature
	if ( SectorInfo[ SECTOR(sSectorX, sSectorY) ].ubTraversability[ THROUGH_STRATEGIC_MOVE ] == SAND ||
		SectorInfo[ SECTOR(sSectorX, sSectorY) ].ubTraversability[ THROUGH_STRATEGIC_MOVE ] == SAND_ROAD ||
		SectorInfo[ SECTOR(sSectorX, sSectorY) ].ubTraversability[ THROUGH_STRATEGIC_MOVE ] == SAND_SAM_SITE )
	{
		// desert
		return( TRUE );
	}
	else
	{
		return( FALSE );
	}
}
// SANDRO - added function
BOOLEAN IsSectorTropical( INT16 sSectorX, INT16 sSectorY )
{
	if (SectorInfo[ SECTOR(sSectorX, sSectorY) ].ubTraversability[ THROUGH_STRATEGIC_MOVE ] == TROPICS ||
		SectorInfo[ SECTOR(sSectorX, sSectorY) ].ubTraversability[ THROUGH_STRATEGIC_MOVE ] == TROPICS_ROAD ||
		SectorInfo[ SECTOR(sSectorX, sSectorY) ].ubTraversability[ THROUGH_STRATEGIC_MOVE ] == TROPICS_SAM_SITE )
	{
		return ( TRUE );
	}
	else
	{
		return ( FALSE ); 
	}
}


BOOLEAN HandleDefiniteUnloadingOfWorld( UINT8 ubUnloadCode )
{
	INT32 i;

	// clear tactical queue
	ClearEventQueue();

	// ATE: End all bullets....
	DeleteAllBullets();

	// End all physics objects...
	RemoveAllPhysicsObjects();

	RemoveAllActiveTimedBombs();

	// handle any quest stuff here so world items can be affected
	HandleQuestCodeOnSectorExit( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );

	//if we arent loading a saved game
	if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
	{
		//Clear any potential battle flags.  They will be set if necessary.
		gTacticalStatus.fEnemyInSector = FALSE;
		gTacticalStatus.uiFlags &= ~INCOMBAT;
	}

	if ( ubUnloadCode == ABOUT_TO_LOAD_NEW_MAP )
	{
		//if we arent loading a saved game
		if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
		{

			// Save the current sectors Item list to a temporary file, if its not the first time in
			SaveCurrentSectorsInformationToTempItemFile();

			// Update any mercs currently in sector, their profile info...
			UpdateSoldierPointerDataIntoProfile( FALSE );
		}
	}
	else if( ubUnloadCode == ABOUT_TO_TRASH_WORLD )
	{
		//Save the current sectors open temp files to the disk
		if( !SaveCurrentSectorsInformationToTempItemFile() )
		{
			ScreenMsg( FONT_MCOLOR_WHITE, MSG_TESTVERSION, L"ERROR in SaveCurrentSectorsInformationToTempItemFile()");
			return FALSE;
		}

		//Setup the tactical existance of the current soldier.
		//@@@Evaluate
		for( i = gTacticalStatus.Team[ CIV_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; i++ )
		{
			if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->bInSector )
			{
				SetupProfileInsertionDataForSoldier( MercPtrs[ i ] );
			}
		}

		gfBlitBattleSectorLocator = FALSE;
	}

	//Handle cases for both types of unloading
	HandleMilitiaStatusInCurrentMapBeforeLoadingNewMap();
	return TRUE;
}

BOOLEAN HandlePotentialBringUpAutoresolveToFinishBattle(int pSectorX, int pSectorY, int pSectorZ)
{
	//We don't have mercs in the sector.  Now, we check to see if there are BOTH enemies and militia.  If both
	//co-exist in the sector, then make them fight for control of the sector via autoresolve.
	for( int i = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID; i <= gTacticalStatus.Team[ CREATURE_TEAM ].bLastID; i++ )
	{
		if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife )
		{
			if( MercPtrs[ i ]->sSectorX == pSectorX &&
					MercPtrs[ i ]->sSectorY == pSectorY &&
					MercPtrs[ i ]->bSectorZ == pSectorZ )
			{ //We have enemies, now look for militia!
				for( i = gTacticalStatus.Team[ MILITIA_TEAM ].bFirstID; i <= gTacticalStatus.Team[ MILITIA_TEAM ].bLastID; i++ )
				{
					if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife && MercPtrs[ i ]->bSide == OUR_TEAM )
					{
						if( MercPtrs[ i ]->sSectorX == pSectorX &&
								MercPtrs[ i ]->sSectorY == pSectorY &&
								MercPtrs[ i ]->bSectorZ == pSectorZ )
						{ //We have militia and enemies and no mercs!  Let's finish this battle in autoresolve.
							gfEnteringMapScreen = TRUE;
							gfEnteringMapScreenToEnterPreBattleInterface = TRUE;
							gfAutomaticallyStartAutoResolve = TRUE;
							gfUsePersistantPBI = FALSE;
							gubPBSectorX = (UINT8)pSectorX;
							gubPBSectorY = (UINT8)pSectorY;
							gubPBSectorZ = (UINT8)pSectorZ;
							gfBlitBattleSectorLocator = TRUE;
							gfTransferTacticalOppositionToAutoResolve = TRUE;
							if( gubEnemyEncounterCode != CREATURE_ATTACK_CODE )
							{
								gubEnemyEncounterCode = ENEMY_INVASION_CODE; //has to be, if militia are here.
							}
							else
							{
								//DoScreenIndependantMessageBox( gzLateLocalizedString[ 39 ], MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback );
							}

              return( TRUE );
						}
					}
				}
			}
		}
  }

  return( FALSE );
}


BOOLEAN CheckAndHandleUnloadingOfCurrentWorld()
{
	INT32 i;
	INT16 sBattleSectorX, sBattleSectorY, sBattleSectorZ;

	//Don't bother checking this if we don't have a world loaded.
	if( !gfWorldLoaded )
	{
		return FALSE;
	}

	if(is_client)//hayden - if multiplayer dont kick from level, allow spectate.
	{
		return FALSE;
	}

	if( gTacticalStatus.fDidGameJustStart && gWorldSectorX == gGameExternalOptions.ubDefaultArrivalSectorX && gWorldSectorY == gGameExternalOptions.ubDefaultArrivalSectorY && !gbWorldSectorZ )
	{
		return FALSE;
	}

	GetCurrentBattleSectorXYZ( &sBattleSectorX, &sBattleSectorY, &sBattleSectorZ );

	if( guiCurrentScreen == AUTORESOLVE_SCREEN )
	{ //The user has decided to let the game autoresolve the current battle.
		if( gWorldSectorX == sBattleSectorX && gWorldSectorY == sBattleSectorY && gbWorldSectorZ == sBattleSectorZ )
		{
			for( i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ )
			{ //If we have a live and valid soldier
				if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife && !MercPtrs[ i ]->flags.fBetweenSectors && !(MercPtrs[ i ]->flags.uiStatusFlags & SOLDIER_VEHICLE ) && !AM_A_ROBOT( MercPtrs[ i ] ) && !AM_AN_EPC(  MercPtrs[ i ] ) )
				{
					if( MercPtrs[ i ]->sSectorX == gWorldSectorX &&
							MercPtrs[ i ]->sSectorY == gWorldSectorY &&
							MercPtrs[ i ]->bSectorZ == gbWorldSectorZ )
					{
						MercPtrs[ i ]->RemoveSoldierFromGridNo(  );
						InitSoldierOppList( MercPtrs[ i ] );
					}
				}
			}
		}
	}
	else
	{	//Check and see if we have any live mercs in the sector.
		for( i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ )
		{ //If we have a live and valid soldier
			if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife && !MercPtrs[ i ]->flags.fBetweenSectors && !(MercPtrs[ i ]->flags.uiStatusFlags & SOLDIER_VEHICLE ) && !AM_A_ROBOT( MercPtrs[ i ] ) && !AM_AN_EPC(  MercPtrs[ i ] ))
			{
				if( MercPtrs[ i ]->sSectorX == gWorldSectorX &&
						MercPtrs[ i ]->sSectorY == gWorldSectorY &&
						MercPtrs[ i ]->bSectorZ == gbWorldSectorZ )
				{
					return FALSE;
				}
			}
		}
		//KM : August 6, 1999 Patch fix
		//     Added logic to prevent a crash when player mercs would retreat from a battle involving militia and enemies.
		//		 Without the return here, it would proceed to trash the world, and then when autoresolve would come up to
		//     finish the tactical battle, it would fail to find the existing soldier information (because it was trashed).
    if( HandlePotentialBringUpAutoresolveToFinishBattle( sBattleSectorX, sBattleSectorY, sBattleSectorZ ) )
		{
			return FALSE;
		}
		//end

		//HandlePotentialBringUpAutoresolveToFinishBattle( ); //prior patch logic
	}


	CheckForEndOfCombatMode( FALSE );
	EndTacticalBattleForEnemy();

	// ATE: Change cursor to wait cursor for duration of frame.....
	// save old cursor ID....
	SetCurrentCursorFromDatabase( CURSOR_WAIT_NODELAY );
	RefreshScreen( NULL );

	// JA2Gold: Leaving sector, so get rid of ambients!
	DeleteAllAmbients();

	if( guiCurrentScreen == GAME_SCREEN )
	{
		if( !gfTacticalTraversal )
		{ //if we are in tactical and don't intend on going to another sector immediately, then
			gfEnteringMapScreen = TRUE;
		}
		else
		{ //The trashing of the world will be handled automatically.
			return FALSE;
		}
	}

	//We have passed all the checks and can Trash the world.
	if( !HandleDefiniteUnloadingOfWorld( ABOUT_TO_TRASH_WORLD ) )
	{
		return FALSE;
	}

	if( guiCurrentScreen == AUTORESOLVE_SCREEN )
	{
		if( gWorldSectorX == sBattleSectorX && gWorldSectorY == sBattleSectorY && gbWorldSectorZ == sBattleSectorZ )
		{	//Yes, this is and looks like a hack.  The conditions of this if statement doesn't work inside
			//TrashWorld() or more specifically, TacticalRemoveSoldier() from within TrashWorld().  Because
			//we are in the autoresolve screen, soldiers are internally created different (from pointers instead of
			//the MercPtrs[]).  It keys on the fact that we are in the autoresolve screen.  So, by switching the
			//screen, it'll delete the soldiers in the loaded world properly, then later on, once autoresolve is
			//complete, it'll delete the autoresolve soldiers properly.  As you can now see, the above if conditions
			//don't change throughout this whole process which makes it necessary to do it this way.
			guiCurrentScreen = MAP_SCREEN;
			TrashWorld();
			guiCurrentScreen = AUTORESOLVE_SCREEN;
		}
	}
	else
	{
		TrashWorld();
	}

	//Clear all combat related flags.
	gTacticalStatus.fEnemyInSector = FALSE;
	gTacticalStatus.uiFlags &= ~INCOMBAT;
  EndTopMessage( );


	//Clear the world sector values.
	gWorldSectorX = gWorldSectorY = 0;
	gbWorldSectorZ = -1;

	//Clear the flags regarding.
	gfCaves = FALSE;
	gfBasement = FALSE;

	//CHRISL: If we're unloading the world, shouldn't be reset the gfWorldLoaded flag?
	gfWorldLoaded = FALSE;

	return TRUE;
}



//This is called just before the world is unloaded to preserve location information for RPCs and NPCs either in
//the sector or strategically in the sector (such as firing an NPC in a sector that isn't yet loaded.)  When loading that
//sector, the RPC would be added.
//@@@Evaluate
void SetupProfileInsertionDataForSoldier( SOLDIERTYPE *pSoldier )
{
	if( !pSoldier || pSoldier->ubProfile == NO_PROFILE )
	{ //Doesn't have profile information.
		return;
	}

	if ( gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags3 & PROFILE_MISC_FLAG3_PERMANENT_INSERTION_CODE )
	//if ( gMercProfiles[ pSoldier->ubProfile ].ubStrategicInsertionCode == INSERTION_CODE_PERMANENT_GRIDNO )
	{
		// can't be changed!
		return;
	}

	if ( gfWorldLoaded && pSoldier->bActive && pSoldier->bInSector )
	{ //This soldier is currently in the sector

					//@@@Evaluate -- insert code here
					//SAMPLE CODE:  There are multiple situations that I didn't code.  The gridno should be the final destination
					//or reset???

		if ( pSoldier->ubQuoteRecord && pSoldier->ubQuoteActionID )
		{
			// if moving to traverse
			if ( pSoldier->ubQuoteActionID >= QUOTE_ACTION_ID_TRAVERSE_EAST && pSoldier->ubQuoteActionID <= QUOTE_ACTION_ID_TRAVERSE_NORTH )
			{
				// Handle traversal.  This NPC's sector will NOT already be set correctly, so we have to call for that too
				HandleNPCChangesForTacticalTraversal( pSoldier );
				gMercProfiles[ pSoldier->ubProfile ].fUseProfileInsertionInfo = FALSE;
				if ( pSoldier->ubProfile != NO_PROFILE && NPCHasUnusedRecordWithGivenApproach( pSoldier->ubProfile, APPROACH_DONE_TRAVERSAL ) )
				{
					gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags3 |= PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL;
				}

			}
			else
			{
				if ( pSoldier->pathing.sFinalDestination == pSoldier->sGridNo )
				{
					gMercProfiles[ pSoldier->ubProfile ].usStrategicInsertionData = pSoldier->sGridNo;
				}				
				else if (!TileIsOutOfBounds(pSoldier->sAbsoluteFinalDestination))
				{
					gMercProfiles[ pSoldier->ubProfile ].usStrategicInsertionData = pSoldier->sAbsoluteFinalDestination;
				}
				else
				{
					gMercProfiles[ pSoldier->ubProfile ].usStrategicInsertionData = pSoldier->pathing.sFinalDestination;
				}

				gMercProfiles[ pSoldier->ubProfile ].fUseProfileInsertionInfo = TRUE;
				gMercProfiles[ pSoldier->ubProfile ].ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
				gMercProfiles[ pSoldier->ubProfile ].ubQuoteActionID = pSoldier->ubQuoteActionID;
				gMercProfiles[ pSoldier->ubProfile ].ubQuoteRecord = pSoldier->ubQuoteActionID;
			}
		}
		else
		{
			gMercProfiles[ pSoldier->ubProfile ].fUseProfileInsertionInfo = FALSE;
		}

	}
	else
	{ //use strategic information

		//It appears to set the soldier's strategic insertion code everytime a group arrives in a new sector.  The insertion data
		//isn't needed for these cases as the code is a direction only.
		gMercProfiles[ pSoldier->ubProfile ].ubStrategicInsertionCode = pSoldier->ubStrategicInsertionCode;
		gMercProfiles[ pSoldier->ubProfile ].usStrategicInsertionData = 0;

		//Strategic system should now work.
		gMercProfiles[ pSoldier->ubProfile ].fUseProfileInsertionInfo = TRUE;
	}
}


void HandlePotentialMoraleHitForSkimmingSectors( GROUP *pGroup )
{
	//PLAYERGROUP *pPlayer;

  if ( !gTacticalStatus.fHasEnteredCombatModeSinceEntering && gTacticalStatus.fEnemyInSector )
  {
		//Flag is set so if "wilderness" enemies are in the adjacent sector of this group, the group has
		//a 90% chance of ambush.  Because this typically doesn't happen very often, the chance is high.
		//This reflects the enemies radioing ahead to other enemies of the group's arrival, so they have
		//time to setup a good ambush!
		pGroup->uiFlags |= GROUPFLAG_HIGH_POTENTIAL_FOR_AMBUSH;

	  //SANDRO - WTF?!?
	  //pPlayer = pGroup->pPlayerList;

	  //while( pPlayer )
	  //{
   //   // Do morale hit...
   //   // CC look here!
   //   // pPlayer->pSoldier

		 // pPlayer = pPlayer->next;
	  //}
  }
}



#ifdef CRIPPLED_VERSION
void CrippledVersionFailureToLoadMapCheck()
{
	CHAR16	zString[512];

	swprintf( zString, L"Error! Sorry, you must stay between sectors A and E in this limited press version." );

	DoScreenIndependantMessageBox( zString, MSG_BOX_FLAG_OK, CrippledVersionFailureToLoadMapCallBack );
}

void CrippledVersionFailureToLoadMapCallBack( UINT8 bExitValue )
{
	//clean up the code
	ReStartingGame();

	//go to the main menu
	if( guiCurrentScreen == MAP_SCREEN )
	{
		SetPendingNewScreen(MAINMENU_SCREEN);
	}
	else
	{
		InternalLeaveTacticalScreen( MAINMENU_SCREEN );
	}
}
#endif
BOOLEAN EscapeDirectionIsValid( INT8 * pbDirection )
{
	switch( *pbDirection )
		{
			case NORTHEAST: if( gWorldSectorX + 1 > MAXIMUM_VALID_X_COORDINATE ) *pbDirection = -1; break; // east
			case SOUTHEAST: if( gWorldSectorY + 1 > MAXIMUM_VALID_Y_COORDINATE ) *pbDirection = -1; break; // south
			case SOUTHWEST: if( gWorldSectorX - 1 < MINIMUM_VALID_X_COORDINATE ) *pbDirection = -1; break; // west
			case NORTHWEST: if( gWorldSectorY - 1 < MINIMUM_VALID_Y_COORDINATE ) *pbDirection = -1; break; // north
			default: *pbDirection = -1;
	}
	return( *pbDirection != -1 );
}
#ifdef JA2UB


//------------ub

typedef struct
{
	INT16	sSectorID;
	INT8	bSectorZ;
	UINT8	ubQuoteNum;
} ENTER_SECTOR_PLAYER_QUOTE;

void HandlePlayerTeamQuotesWhenEnteringSector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ )
{
	UINT32	uiCnt;
	INT8		bNumValidMercs=-1;
	INT32		iSectorID = SECTOR( sSectorX, sSectorY );
	UINT8		ubValidMercProfileIDArray[ NUM_MERCS_WITH_NEW_QUOTES ];
	SOLDIERTYPE *pSoldier=NULL;


	#define	NUM_VALID_SECTORS									6
	#define	DELAY_FOR_PLAYER_DESC_OF_SECTOR		2
	ENTER_SECTOR_PLAYER_QUOTE	PlayerSectorDescQuote[ NUM_VALID_SECTORS ] = 
	{
		{ SEC_H9,		0,	QUOTE_HATE_MERC_1_ON_TEAM },
		{ SEC_I9,		0,	QUOTE_LEARNED_TO_HATE_MERC_ON_TEAM },
		{ SEC_H10,	0,	QUOTE_LEARNED_TO_HATE_MERC_ON_TEAM },
		{ SEC_I10,	0,	QUOTE_HATE_MERC_2_ON_TEAM },
		{ SEC_J13,	0,	QUOTE_ENTER_SECTOR_WITH_FAN_1 },
		{ SEC_J14,	1,	0 },
	};

	//loop through all the sectors that have the quotes
	for( uiCnt=0; uiCnt<NUM_VALID_SECTORS; uiCnt++ )
	{
		//if this sector is the right x & y
		if( PlayerSectorDescQuote[ uiCnt ].sSectorID == iSectorID )
		{
			//if is the right level
			if( PlayerSectorDescQuote[ uiCnt ].bSectorZ == sSectorZ )
			{

				// If the player has never said the "enter sector" desc before
				if( GetSectorFlagStatus( sSectorX, sSectorY, ( UINT8 )sSectorZ, SF_HAVE_SAID_PLAYER_QUOTE_NEW_SECTOR ) != TRUE )
				{
					//Get # of valid mercs
					bNumValidMercs = GetNumSoldierIdAndProfileIdOfTheNewMercsOnPlayerTeam( NULL, ubValidMercProfileIDArray );

					//if there isnt any valid mercs
					if( bNumValidMercs == 0 )
					{
						//exit
						return;
					}

					//Switch on the town ID
					switch( iSectorID )
					{
						case SEC_I10:
							{
								UINT8	cnt;
	
								for( cnt=0; cnt<bNumValidMercs; cnt++ )
								{
									//Say the qoute in a couple of seconds
									DelayedMercQuote( ubValidMercProfileIDArray[ cnt ], PlayerSectorDescQuote[ uiCnt ].ubQuoteNum, GetWorldTotalSeconds() + DELAY_FOR_PLAYER_DESC_OF_SECTOR + cnt );
								}
							}
							break;

						case SEC_J13:
							if( PlayerSectorDescQuote[ uiCnt ].bSectorZ == 0 )
							{
								//See if Manuel is on the team
								pSoldier = FindSoldierByProfileID( 60, TRUE );

								//if he is ON the team
								if( pSoldier != NULL )
								{
									//get manuel to say the quote
									DelayedMercQuote( 60, PlayerSectorDescQuote[ uiCnt ].ubQuoteNum, GetWorldTotalSeconds() + DELAY_FOR_PLAYER_DESC_OF_SECTOR );

									//get manuel to say his additional quote
									DelayedMercQuote( 60, QUOTE_ENTER_SECTOR_WITH_FAN_2, GetWorldTotalSeconds() + DELAY_FOR_PLAYER_DESC_OF_SECTOR );
								}
								else
								{
									//else if Biggens is not on the team, check for biggens
									pSoldier = FindSoldierByProfileID( 61, TRUE );

									//if he is ON the team
									if( pSoldier != NULL )
									{
										//get manuel to say the quote
										DelayedMercQuote( 61, PlayerSectorDescQuote[ uiCnt ].ubQuoteNum, GetWorldTotalSeconds() + DELAY_FOR_PLAYER_DESC_OF_SECTOR );

										//Remember he said
										SetJa25GeneralFlag( JA_GF__BIGGENS_SAID_QUOTE_117 );
									}
								}

								//Remeber the quote was said
								SetFactTrue( FACT_PLAYER_KNOWS_ABOUT_FAN_STOPPING );
							}
							break;
						case SEC_J14:
							{
								//first underground sector
								HandlePlayerQuotesWhenEnteringFirstTunnelSector();
							}
							break;
						default:
							{
								//Get a random merc from the list
								UINT8	ubMercToSayQuote = RandomProfileIdFromNewMercsOnPlayerTeam();

								//Say the qoute in a couple of seconds
								DelayedMercQuote( ubMercToSayQuote, PlayerSectorDescQuote[ uiCnt ].ubQuoteNum, GetWorldTotalSeconds() + DELAY_FOR_PLAYER_DESC_OF_SECTOR );
							}
							break;
					}

					//if this sector is one of the fields before the town, only say the quote once for both sectors
					if( ( iSectorID == SEC_I9 || iSectorID == SEC_H10 ) && sSectorZ == 0 )
					{
						//Remeber that we said the quote
						SetSectorFlag( 9, 9, 0, SF_HAVE_SAID_PLAYER_QUOTE_NEW_SECTOR );
						SetSectorFlag( 10, 8, 0, SF_HAVE_SAID_PLAYER_QUOTE_NEW_SECTOR );
					}

					//default
					else
					{
						//Remeber that we said the quote
						SetSectorFlag( sSectorX, sSectorY, ( UINT8 )sSectorZ, SF_HAVE_SAID_PLAYER_QUOTE_NEW_SECTOR );
					}
				}

				//we are done here, leave
				return;
			}
		}
	}
}

void HandlePlayerQuotesWhenEnteringFirstTunnelSector()
{
	UINT8 ubID;

	//if the player got through using the timed method
	if( gJa25SaveStruct.ubHowPlayerGotThroughFan == PG__PLAYER_STOPPED_FAN_TO_GET_THROUGH )
	{
		UINT8 ubNumPlayersInSector = NumActiveAndConsciousTeamMembers( OUR_TEAM );
		UINT8 ubNumPlayersOnTeam = NumberOfMercsOnPlayerTeam();

		//Get a random qualified merc to say the quote
		ubID = RandomSoldierIdFromNewMercsOnPlayerTeam();

		//if there is no valid merc
		if( ubID == -1 )
			return;

		//if not all the players made it through
		if( ubNumPlayersInSector < ubNumPlayersOnTeam )
		{
//			TacticalCharacterDialogue( &Menptr[ ubID ], QUOTE_CONTRACTS_OVER );
			DelayedMercQuote( Menptr[ ubID ].ubProfile, QUOTE_CONTRACTS_OVER, GetWorldTotalSeconds() + DELAY_FOR_PLAYER_DESC_OF_SECTOR );
		}

		//if ALL mercs made it through
		else if( ubNumPlayersInSector == ubNumPlayersOnTeam )
		{
			DelayedMercQuote( Menptr[ ubID ].ubProfile, QUOTE_CONTRACT_ACCEPTANCE, GetWorldTotalSeconds() + DELAY_FOR_PLAYER_DESC_OF_SECTOR );
//			TacticalCharacterDialogue( &Menptr[ ubID ], QUOTE_CONTRACT_ACCEPTANCE );
		}
	}

	else if( gJa25SaveStruct.ubHowPlayerGotThroughFan == PG__PLAYER_BLEW_UP_FAN_TO_GET_THROUGH )
	{
/*
Chnaged to be played when fan blows up
		//Get a random qualified merc to say the quote
		ubID = RandomSoldierIdFromNewMercsOnPlayerTeam();

		DelayedMercQuote( Menptr[ ubID ].ubProfile, QUOTE_ACCEPT_CONTRACT_RENEWAL, GetWorldTotalSeconds() + DELAY_FOR_PLAYER_DESC_OF_SECTOR );
*/
	}

	else
	{
		Assert( 0 );
	}
}

void HandleEmailBeingSentWhenEnteringSector( INT16 sMapX, INT16 sMapY, INT8 bMapZ, BOOLEAN fLaptopJustGotFixed )
{
	SOLDIERTYPE *pSoldier=NULL;

	//
	// if this sector is a sector we are to send an email to the player from
	//

	//if the laptop transmiter is not working yet
	if( gubQuest[ QUEST_FIX_LAPTOP ] != QUESTDONE && !fLaptopJustGotFixed && gGameLegionOptions.LaptopQuestEnabled == TRUE )
	{
		//we will send these emails later
		return;
	}

	//if miguel is alive
	if( gubFact[ FACT_PLAYER_IMPORTED_SAVE_MIGUEL_DEAD ] == FALSE )
	{
		//if its either J11 or I12 ( or we just got the email back up and we have been to the sector
		if( ( ( sMapY == 10 && sMapX == 11 ) || ( sMapY == 9 && sMapX == 12 ) && bMapZ == 0 ) ||
			fLaptopJustGotFixed && 
			( GetSectorFlagStatus( 11, 10, 0, SF_HAS_ENTERED_TACTICAL ) == TRUE || GetSectorFlagStatus( 12, 9, 0, SF_HAS_ENTERED_TACTICAL ) == TRUE ) )
		{
			//and we havent sent it before
			if( !( gJa25SaveStruct.ubEmailFromSectorFlag & SECTOR_EMAIL__J11_J12 ) )
			{
				pSoldier = FindSoldierByProfileID( 60, TRUE ); //MANUEL

				//if Manuel isnt on the team
				if( pSoldier == NULL || gMercProfiles[ 60 ].bMercStatus == MERC_IS_DEAD ) //MANUEL
				{
					//email 8a
					AddEmail( EMAIL_MIGUELSORRY, EMAIL_MIGUELSORRY_LENGTH, MAIL_MIGUEL,  GetWorldTotalMin() , -1,-1 ,TYPE_EMAIL_EMAIL_EDT);
				}
				else
				{
					//email 8b
					AddEmail( EMAIL_MIGUELMANUEL, EMAIL_MIGUELMANUEL_LENGTH, MAIL_MIGUEL,  GetWorldTotalMin() , -1,-1, TYPE_EMAIL_EMAIL_EDT);
				}

				//Remeber we sent it
				gJa25SaveStruct.ubEmailFromSectorFlag |= SECTOR_EMAIL__J11_J12;
			}
		}

		//if its the power generator sector
		if( sMapY == 10 && sMapX == 13 && bMapZ == 0 ||
			fLaptopJustGotFixed && GetSectorFlagStatus( 13, 10, 0, SF_HAS_ENTERED_TACTICAL ) == TRUE )
		{
			//and we havent sent it before
			if( !( gJa25SaveStruct.ubEmailFromSectorFlag & SECTOR_EMAIL__POWER_GEN ) )
			{
				AddEmail( EMAIL_MIGUELSICK, EMAIL_MIGUELSICK_LENGTH, MAIL_MIGUEL,  GetWorldTotalMin(), -1 ,-1, TYPE_EMAIL_EMAIL_EDT);

				//Remeber we sent it
				gJa25SaveStruct.ubEmailFromSectorFlag |= SECTOR_EMAIL__POWER_GEN;
			}
		}
	}


	//if its the tunnel sector
	if( sMapY == 10 && sMapX == 14 && bMapZ == 1 ||
		fLaptopJustGotFixed && GetSectorFlagStatus( 14, 10, 1, SF_HAS_ENTERED_TACTICAL ) == TRUE	)
	{
		//and we havent sent it before
		if( !( gJa25SaveStruct.ubEmailFromSectorFlag & SECTOR_EMAIL__TUNNEL ) )
		{
			//If Jerry isnt dead
			if( gMercProfiles[ 76 ].bMercStatus != MERC_IS_DEAD ) //JERRY
			{
				AddEmail( EMAIL_PILOTFOUND, EMAIL_PILOTFOUND_LENGTH, MAIL_ENRICO,  GetWorldTotalMin() , -1, -1, TYPE_EMAIL_EMAIL_EDT);
			}

			//Remeber we sent it
			gJa25SaveStruct.ubEmailFromSectorFlag |= SECTOR_EMAIL__TUNNEL;
		}
	}
}



void ShouldNpcBeAddedToSector( INT16 sMapX, INT16 sMapY, INT8 bMapZ )
{
	//if Manuel has never been added before
	if( !( gJa25SaveStruct.fNpcHasBeenAdded & SECTOR_ADDED_NPC__MANUEL) )
	{
		//if it is the right sector
		if( ( sMapY == MAP_ROW_H && sMapX == 10 && bMapZ == 0 ) ||
				( sMapY == MAP_ROW_I && sMapX == 9 && bMapZ == 0 ) )
		{
			//Change his sector values to 
			gMercProfiles[ 60 ].sSectorX = sMapX;
			gMercProfiles[ 60 ].sSectorY = sMapY;
			gMercProfiles[ 60 ].bSectorZ = bMapZ;

			//remember that we have added him
			gJa25SaveStruct.fNpcHasBeenAdded |= SECTOR_ADDED_NPC__MANUEL;
		}
	}

	//if Tex has never been added before 
	if( !( gJa25SaveStruct.fNpcHasBeenAdded & SECTOR_ADDED_NPC__TEX ) )
	{
		//and tex is TO be added
		if( gubFact[ FACT_TEX_IS_IN_GAME_AND_ALIVE_IN_STORE ] )
		{
			//if it is the right sector
			if( sMapY == MAP_ROW_I && sMapX == 10 && bMapZ == 0 )
			{
				//Change his sector values to 
				gMercProfiles[ 64 ].sSectorX = sMapX;
				gMercProfiles[ 64 ].sSectorY = sMapY;
				gMercProfiles[ 64 ].bSectorZ = bMapZ;

				//remember that we have added him
				gJa25SaveStruct.fNpcHasBeenAdded |= SECTOR_ADDED_NPC__TEX;
			}
		}
	}

	//if John has never been added before 
	if( !( gJa25SaveStruct.fNpcHasBeenAdded & SECTOR_ADDED_NPC__JOHN_K ) )
	{
		//and John is TO be added ( Tex not in game )
		//if( !gubFact[ FACT_TEX_IS_IN_GAME_AND_ALIVE_IN_STORE ] )
	//	{
			//if it is the right sector
			if( sMapX == gJa25SaveStruct.ubJohnKulbaInitialSectorX && 
					sMapY == gJa25SaveStruct.ubJohnKulbaInitialSectorY && 
					bMapZ == 0 )
			{
				//Change his sector values to 
				gMercProfiles[ 62 ].sSectorX = sMapX;
				gMercProfiles[ 62 ].sSectorY = sMapY;
				gMercProfiles[ 62 ].bSectorZ = bMapZ;

				//remember that we have added him
				gJa25SaveStruct.fNpcHasBeenAdded |= SECTOR_ADDED_NPC__JOHN_K;
			}
	//	}
	}
}

void HandleSectorSpecificUnLoadingOfMap( INT16 sMapX, INT16 sMapY, INT8 bMapZ )
{
	//if this is the power gen map
	if( sMapX == 13 && sMapY == MAP_ROW_J && bMapZ == 0 )
	{
		BOOLEAN fGoingToTunnelSector=FALSE;

		//if we are going to the first sector if the tunnel
		if( gWorldSectorX == 14 &&
				gWorldSectorY == MAP_ROW_J &&
				gbWorldSectorZ == 1 )
		{
			fGoingToTunnelSector = TRUE;
		}

		switch( gJa25SaveStruct.ubStateOfFanInPowerGenSector )
		{
			case PGF__RUNNING_NORMALLY:
				HandleRemovingPowerGenFanSound();
				break;
		}

		//Remeber how the player got through
		HandleHowPlayerGotThroughFan();
	}
	//else if this is the 1st level of tunne;l
	else	if( sMapX == 14 && sMapY == MAP_ROW_J && bMapZ == 1 )
	{
		switch( gJa25SaveStruct.ubStateOfFanInPowerGenSector )
		{
			case PGF__RUNNING_NORMALLY:
			case PGF__STOPPED:

				//remove the sound to the world
				HandleRemovingPowerGenFanSound();
				break;
		}
	}
}


void HandleSectorSpecificModificatioToMap( INT16 sMapX, INT16 sMapY, INT8 bMapZ, BOOLEAN fLoadingSavedGame )
{
//	SOLDIERTYPE *pSoldier=NULL;
//	INT32				iCash=0;

	//if we are loading a game, dont do this yet ( it will be done after everything is loaded
	if( gTacticalStatus.uiFlags & LOADING_SAVED_GAME )
	{
		return;
	}

	SetTileAnimCounter( TILE_ANIM__NORMAL_SPEED );

	//if we are being called from LoadSavedGame()
	if( fLoadingSavedGame )
	{
		//
		// only do certain modifications
		//

		//if this is the power gen map
		if( sMapX == 13 && sMapY == MAP_ROW_J && bMapZ == 0 )
		{
			HandlePowerGenFanSoundModification();
		}
		else	if( sMapX == 14 && sMapY == MAP_ROW_J && bMapZ == 1 )
		{
			HandleFirstPartOfTunnelFanSound();
		}

		//if this is the Final Sector of the complex
		else if( sMapX == 15 && sMapY == MAP_ROW_L && bMapZ == 3 )
		{
			HandleOpenControlPanelToRevealSwitchInMorrisArea();
		}
	}
	else
	{
		//if this is the first map
		if( sMapX == 7 && sMapY == MAP_ROW_H && bMapZ == 0 )
		{
		}

		//if this is the guardpost
		else if( sMapX == 9 && sMapY == MAP_ROW_H && bMapZ == 0 )
		{
			//if we havent added the money to the sector before
			if( !IsJa25GeneralFlagSet( JA_GF__PICKED_UP_MONEY_IN_GUARD_POST ) )
			{
				SetJa25GeneralFlag( JA_GF__PICKED_UP_MONEY_IN_GUARD_POST );

				// Add some money to the location
				CreateAndAddMoneyObjectToGround( 9026, 15000, 10000, 7000 );
			}
		}

		//if this is the First sector of the town
		else if( sMapX == 10 && sMapY == MAP_ROW_I && bMapZ == 0 )
		{
			//if we havent added the money to the sector before
			if( !IsJa25GeneralFlagSet( JA_GF__PICKED_UP_MONEY_IN_FIRST_TOWN ) )
			{
				SetJa25GeneralFlag( JA_GF__PICKED_UP_MONEY_IN_FIRST_TOWN );

				// Add some money to the location
				CreateAndAddMoneyObjectToGround( 11894, 8000, 4000, 3000 );
				CreateAndAddMoneyObjectToGround( 7906,  12000, 6000, 5000 );
			}
		}

		//if this is the power gen map
		else if( sMapX == 13 && sMapY == MAP_ROW_J && bMapZ == 0 )
		{
			HandlePowerGenFanSoundModification();
		}

		//else if this is the 1st part of tunnel
		else	if( sMapX == 14 && sMapY == MAP_ROW_J && bMapZ == 1 )
		{
			HandleFirstPartOfTunnelFanSound();

			if( IsJa25GeneralFlagSet( JA_GF__MOVE_ENEMIES_TO_EDGE_IN_TUNNEL_1 ) )
			{
				HandleMovingEnemiesCloseToEntranceInFirstTunnelMap();
			}
		}

		//else if this is the 2nd part of tunnel
		else	if( sMapX == 14 && sMapY == MAP_ROW_K && bMapZ == 1 )
		{
			if( IsJa25GeneralFlagSet( JA_GF__MOVE_ENEMIES_TO_EDGE_IN_TUNNEL_1 ) )
			{
				HandleMovingEnemiesCloseToEntranceInSecondTunnelMap();
			}
		}

		//else if this is the 1st level in the complex
		else	if( sMapX == 15 && sMapY == MAP_ROW_K && bMapZ == 1 )
		{
			//Make all the team members look like they dropped from a high place
			//		MakeAllTeamMembersCrouchedThenStand();

			//If the player made noise in the tunnel, enemies should be placed near them
			HandleMovingTheEnemiesToBeNearPlayerWhenEnteringComplexMap();

			//if the big door should be opened
			HandleFortifiedDoor();
		}

		//if this is the Final Sector of the complex
		else if( sMapX == 15 && sMapY == MAP_ROW_L && bMapZ == 3 )
		{
			HandleOpenControlPanelToRevealSwitchInMorrisArea();
		}

		
		
		
		//if the enemies should go and find the player mercs
		if( GetSectorEnemyIsToImmediatelySeekEnemyIn() != -1 )
		{
			//Make the enemies go find the player mercs
			SetEnemiesToFindThePlayerMercs();
		}

		//if this is a sector we feel can be made harder for players ( on hard difficulty levels ), then move some
		//enemies onto roofs.
		HandleMovingEnemiesOntoRoofs();		
	}
}
/*
void MakeAllTeamMembersCrouchedThenStand()
{
	UINT8	cnt;
	SOLDIERTYPE *pSoldier=NULL;

	//Move some of the enemies to be 'near' them player when the enter the room
	cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID;

	// Loop through the list and move some of the enemies
	for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++, pSoldier++)
	{	
		//if the soldier is active,
		if( pSoldier->bActive && pSoldier->bInSector )
		{
			pSoldier->EVENT_InitNewSoldierAnim( CRAWLING, 1, TRUE );
			pSoldier->ChangeSoldierStance( PRONE );
		}
	}
}
*/
void HandleMovingTheEnemiesToBeNearPlayerWhenEnteringComplexMap()
{
	SOLDIERTYPE *pSoldier=NULL;
	UINT8				ubNumEnemiesMoved=0;

	//if we are loading a saved game, or we have already moved the enemies, get out.
	if( gTacticalStatus.uiFlags & LOADING_SAVED_GAME || 
			gJa25SaveStruct.uiJa25GeneralFlags & JA_GF__ALREADY_MOVED_ENEMIES_IN_COMPLEX )
	{
		return;
	}

	//if the player made a 'noise' going through the gate at the end of the tunnel sector
	if( gJa25SaveStruct.uiJa25GeneralFlags & JA_GF__DID_PLAYER_MAKE_SOUND_GOING_THROUGH_TUNNEL_GATE )
	{
		UINT8	cnt;

		//
		//Move some of the enemies to be 'near' them player when the enter the room
		//

		// Loop through the list and move some of the enemies
		cnt = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID;
		for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; cnt++, pSoldier++)
		{	
			//if the soldier is active,
			if ( pSoldier->bActive  )
			{
				//
				// move the soldier to the modified location
				//

				if( pSoldier->sGridNo	== 13959 )
				{
					 pSoldier->SetSoldierGridNo( 15705, TRUE );
					ubNumEnemiesMoved++;
				}

				if( pSoldier->sGridNo	== 13983 )
				{
					pSoldier->SetSoldierGridNo( 15712, TRUE );
					ubNumEnemiesMoved++;
				}

				if( pSoldier->sGridNo	== 12543 )
				{
					pSoldier->SetSoldierGridNo( 15233, TRUE );
					ubNumEnemiesMoved++;
				}
			}
		}

		while( ubNumEnemiesMoved < 3 )
		{
			cnt = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID;
			for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; cnt++, pSoldier++)
			{	
				//if the soldier is active,
				if ( pSoldier->bActive  && pSoldier->sGridNo != 15705 && pSoldier->sGridNo != 15712 && pSoldier->sGridNo != 15233 )
				{
					//
					// move the soldier to the modified location
					//

					if( ubNumEnemiesMoved == 0 )
					{
						pSoldier->SetSoldierGridNo( 15706, TRUE );
						ubNumEnemiesMoved++;
					}

					if( ubNumEnemiesMoved == 1 )
					{
						pSoldier->SetSoldierGridNo( 15713, TRUE );
						ubNumEnemiesMoved++;
					}

					if( ubNumEnemiesMoved == 2 )
					{
						pSoldier->SetSoldierGridNo( 15234, TRUE );
						ubNumEnemiesMoved++;
					}
				}
			}
		}

		//Remeber we have moved the enemies
		gJa25SaveStruct.uiJa25GeneralFlags |= JA_GF__ALREADY_MOVED_ENEMIES_IN_COMPLEX;
	}
}

void HandleFortifiedDoor()
{
	//if the fortified door should be open
	if( gJa25SaveStruct.ubStatusOfFortifiedDoor == FD__OPEN )
	{
		ModifyDoorStatus( 11419, TRUE, DONTSETDOORSTATUS );
	}
}

void CreateAndAddMoneyObjectToGround( UINT32 sGridNo, INT32 iEasyAmount, INT32 iNormalAmount, INT32 iHardAmount )
{
	OBJECTTYPE	Object;
	INT32				iCash=0;
//	INT8				bAmountToAdd=0;

	switch( gGameOptions.ubDifficultyLevel )
	{
		case DIF_LEVEL_EASY:
			iCash = iEasyAmount;
			break;

		case DIF_LEVEL_MEDIUM:
			iCash = iNormalAmount;
			break;

		case DIF_LEVEL_HARD:
			iCash = iHardAmount;
			break;

		default:
			Assert(0);
	}

	CreateMoney( iCash, &Object );

	//add the item to the world
	AddItemToPool( sGridNo, &Object, FALSE, 0, 0, 0 );
}


void HandleGoingUpOrDownStairsForLoadScreensPurposes( INT16 sCurrentlyInSectorZ, INT16 sGoingToSectorZ )
{
	if( sCurrentlyInSectorZ == sGoingToSectorZ )
	{
		gJa25SaveStruct.ubLoadScreenStairTraversal = LS__NOT_GOING_UP_STAIRS;
	}
	else if( sCurrentlyInSectorZ < sGoingToSectorZ )
	{
		gJa25SaveStruct.ubLoadScreenStairTraversal = LS__GOING_DOWN_STAIRS;
	}
	else
	{
		gJa25SaveStruct.ubLoadScreenStairTraversal = LS__GOING_UP_STAIRS;
	}
}

void HandleMovingEnemiesCloseToEntranceInFirstTunnelMap()
{
	SOLDIERTYPE *pSoldier=NULL;
	UINT8	ubIndex=0;
	UINT32 cnt;
	BOOLEAN	fDone=FALSE;
	INT16							sXPos, sYPos;
	UINT32 sGridNos[27]={ 18200, 18360, 18520,
											 18199, 18359, 18519,
											 18198, 18358, 18518,
											 18197, 18357, 18517,
											 18196, 18356, 18516,
											 18195, 18355, 18515,
											 18194, 18354, 18514,
											 18193, 18353, 18513,
											 18035, 18034, 18033,
	};


	//
	//Move some of the enemies to be 'near' them player when the enter the room
	//

	// Loop through the list and move some of the enemies
	cnt = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID;
	for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; cnt++, pSoldier++)
	{	
		//if the soldier is active,
		if ( pSoldier->bActive  )
		{
			fDone = FALSE;
			while( !fDone )
			{
				//if there is no one in the gridno
				if( WhoIsThere2( sGridNos[ ubIndex ], 0 ) == NOBODY )
				{
					// move the soldier to the modified location
					ConvertGridNoToCenterCellXY( sGridNos[ ubIndex ], &sXPos, &sYPos );
					pSoldier->EVENT_SetSoldierPosition( sXPos, sYPos );
//					SetSoldierGridNo( pSoldier, sGridNos[ ubIndex ], TRUE );
					ubIndex++;
					fDone=TRUE;
				}
				else
				{
					ubIndex++;
				}

				if( ubIndex >= 27 )
				{
					Assert( 0 );
					return;
				}
			}
		}
	}
}

void HandleMovingEnemiesCloseToEntranceInSecondTunnelMap()
{
	SOLDIERTYPE *pSoldier=NULL;
	UINT8	ubIndex=0;
	BOOLEAN	fDone=FALSE;
	UINT32 cnt;
	INT16							sXPos, sYPos;
	UINT32 sGridNos[30]={ 4900, 4901, 4902, 4903, 4904, 
											 5060, 5061, 5062, 5063, 5064,
											 5220, 5221, 5222, 5223, 5224,
											 5380, 5381, 5382, 5383, 5384,
											 5540, 5541, 5542, 5543, 5544,
											 5700, 5701, 5702, 5703, 5704 };



	//
	//Move some of the enemies to be 'near' them player when the enter the room
	//

	// Loop through the list and move some of the enemies
	cnt = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID;
	for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; cnt++, pSoldier++)
	{	
		//if the soldier is active,
		if ( pSoldier->bActive  )
		{
			fDone = FALSE;
			while( !fDone )
			{
				//if there is no one in the gridno
				if( WhoIsThere2( sGridNos[ ubIndex ], 0 ) == NOBODY )
				{
					// move the soldier to the modified location
					ConvertGridNoToCenterCellXY( sGridNos[ ubIndex ], &sXPos, &sYPos );
					pSoldier->EVENT_SetSoldierPosition( sXPos, sYPos );
//					SetSoldierGridNo( pSoldier, sGridNos[ ubIndex ], TRUE );
					ubIndex++;
					fDone=TRUE;
				}
				else
				{
					ubIndex++;
				}

				if( ubIndex >= 26 )
				{
					Assert( 0 );
					return;
				}
			}
		}
	}
}

void HandlePowerGenFanSoundModification()
{
	SetTileAnimCounter( TILE_ANIM__FAST_SPEED );

	switch( gJa25SaveStruct.ubStateOfFanInPowerGenSector )
	{
		case PGF__RUNNING_NORMALLY:
			HandleAddingPowerGenFanSound();

			//MAKE SURE the fan does not have an exit grid
			RemoveExitGridFromWorld( PGF__FAN_EXIT_GRID_GRIDNO );
			break;
		
		case PGF__STOPPED:
			//Add an exit grid to the map
			AddExitGridForFanToPowerGenSector();
			break;
		
		case PGF__BLOWN_UP:
			break;

		default:
			Assert( 0 );
	}
}

void HandleFirstPartOfTunnelFanSound()
{
	switch( gJa25SaveStruct.ubStateOfFanInPowerGenSector )
	{
		case PGF__RUNNING_NORMALLY:
		case PGF__STOPPED:

			//add the sound to the world
			HandleAddingPowerGenFanSound();
			break;
	}
}

void HandleMovingEnemiesOntoRoofs()
{
	if( gWorldSectorX <= 0 || gWorldSectorY <= 0 || gbWorldSectorZ < 0 )
	{
		return;
	}

	//if this is the sector south of the town
	if( gWorldSectorX == 11 && gWorldSectorY == MAP_ROW_J && gbWorldSectorZ == 0 )
	{
		switch( gGameOptions.ubDifficultyLevel )
		{
			case DIF_LEVEL_EASY:
			case DIF_LEVEL_MEDIUM:
				break;
			case DIF_LEVEL_HARD:
				MoveEnemyFromGridNoToRoofGridNo( 15446, 13993 );
				MoveEnemyFromGridNoToRoofGridNo( 15436, 14006 );
				break;
		}
	}

	//else if this is the sector south of the town
	else if( gWorldSectorX == 11 && gWorldSectorY == MAP_ROW_H && gbWorldSectorZ == 0 )
	{
		switch( gGameOptions.ubDifficultyLevel )
		{
			case DIF_LEVEL_EASY:
			case DIF_LEVEL_MEDIUM:
				break;
			case DIF_LEVEL_HARD:
				MoveEnemyFromGridNoToRoofGridNo( 8711, 5521 );
				break;
		}
	}
}



BOOLEAN MoveEnemyFromGridNoToRoofGridNo( UINT32 sSourceGridNo, UINT32 sDestGridNo )
{
	SOLDIERTYPE *pSoldier=NULL;
	INT32				cnt;
	INT16				sXPos, sYPos;

	cnt = gTacticalStatus.Team[ ENEMY_TEAM ].bFirstID;
  for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ ENEMY_TEAM ].bLastID; cnt++, pSoldier++)
	{	
		if( pSoldier->stats.bLife >= OKLIFE && pSoldier->bActive && pSoldier->bInSector &&
				pSoldier->sGridNo == sSourceGridNo )
		{
			pSoldier->SetSoldierHeight( 50.0 );

			// move soldier
			ConvertGridNoToCenterCellXY( sDestGridNo, &sXPos, &sYPos );
			pSoldier->EVENT_SetSoldierPosition( sXPos, sYPos );

			return( TRUE );
//			pSoldier->bOrders = SEEKENEMY;
		}
	}

	return( FALSE );
}
#endif